From b8bc282b94f26ce368da7ea4460fd391314b6335 Mon Sep 17 00:00:00 2001 From: Igor Timofeev Date: Sat, 19 Jan 2019 11:31:04 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D1=85=D0=BE=D0=B4?= =?UTF-8?q?=D0=B8=D0=BC=20=D0=BD=D0=B0=20MineOS=20Standalone=20#1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 +- .../{3DPrint => 3D Print.app}/Icon.pic | Bin .../Localizations/English.lang | 0 .../Localizations/French.lang | 0 .../Localizations/Russian.lang | 0 .../Localizations/Ukrainian.lang | 0 .../{3DPrint => 3D Print.app}/Main.lua | 92 +- Applications/{3DTest => 3D Test.app}/Icon.pic | Bin .../3DTest.lua => 3D Test.app/Main.lua} | 181 +- Applications/3DTest/About/English.txt | 1 - Applications/3DTest/About/Russian.txt | 1 - Applications/App Market.app/Icon.pic | Bin 0 -> 117 bytes .../Localizations}/English.lang | 1 + .../Localizations}/French.lang | 1 + .../Localizations}/Russian.lang | 1 + .../Localizations}/Ukrainian.lang | 3 +- .../{AppMarket => App Market.app}/Main.lua | 1225 +++-- Applications/AppMarket/Icon.pic | Bin 117 -> 0 bytes Applications/AppMarket/Update.pic | Bin 2689 -> 0 bytes Applications/Battleship/About/English.txt | 1 - Applications/Battleship/About/Russian.txt | 1 - Applications/Battleship/Battleship.lua | 339 -- Applications/Battleship/Icon.pic | Bin 106 -> 0 bytes Applications/Braille/About/English.txt | 1 - Applications/Braille/About/Russian.txt | 1 - Applications/Braille/Icon.pic | Bin 171 -> 0 bytes Applications/Braille/Main.lua | 262 - Applications/BufferDemo/About/English.txt | 1 - Applications/BufferDemo/About/Russian.txt | 1 - Applications/BufferDemo/BufferDemo.lua | 160 - Applications/BufferDemo/Icon.pic | Bin 108 -> 0 bytes Applications/BufferDemo/Wallpaper.pic | Bin 8639 -> 0 bytes Applications/Calc/Calc.lua | 61 - Applications/Calc/Icon.pic | Bin 277 -> 0 bytes .../{Calculator => Calculator.app}/Icon.pic | Bin .../{Calculator => Calculator.app}/Main.lua | 36 +- Applications/Calendar/About/English.txt | 1 - Applications/Calendar/About/Russian.txt | 1 - Applications/Calendar/Icon.pic | Bin 165 -> 0 bytes Applications/Calendar/Main.lua | 379 -- Applications/{Camera => Camera.app}/Icon.pic | Bin Applications/{Camera => Camera.app}/Main.lua | 38 +- Applications/Chat/About/English.txt | 1 - Applications/Chat/About/Russian.txt | 1 - Applications/Chat/Chat.lua | 777 --- Applications/Chat/Icon.pic | Bin 91 -> 0 bytes Applications/Chat/MyAvatar.pic | Bin 87 -> 0 bytes Applications/Christmas Tree.app/Icon.pic | Bin 0 -> 160 bytes Applications/Christmas Tree.app/Main.lua | 160 + Applications/ChristmasTree/About/English.txt | 1 - Applications/ChristmasTree/About/Russian.txt | 1 - Applications/ChristmasTree/ChristmasTree.lua | 148 - Applications/ChristmasTree/Icon.pic | Bin 152 -> 0 bytes Applications/CodeDoor/About/English.txt | 1 - Applications/CodeDoor/About/Russian.txt | 1 - Applications/CodeDoor/CodeDoor.lua | 276 - Applications/CodeDoor/Icon.pic | Bin 115 -> 0 bytes Applications/Control.app/Icon.pic | Bin 0 -> 168 bytes .../Localizations}/English.lang | 0 .../Localizations}/French.lang | 0 .../Localizations}/Russian.lang | 0 Applications/Control.app/Main.lua | 58 + .../{Control => Control.app}/Modules/1.lua | 23 +- .../{Control => Control.app}/Modules/2.lua | 35 +- .../{Control => Control.app}/Modules/3.lua | 22 +- .../{Control => Control.app}/Modules/4.lua | 19 +- Applications/Control/Icon.pic | Bin 168 -> 0 bytes Applications/Control/Main.lua | 61 - Applications/Crossword/About/English.txt | 1 - Applications/Crossword/About/Russian.txt | 1 - Applications/Crossword/Crossword.lua | 294 -- Applications/Crossword/CrosswordFile.txt | 12 - Applications/Crossword/Icon.pic | Bin 308 -> 0 bytes Applications/DanceFloor/About/English.txt | 1 - Applications/DanceFloor/About/Russian.txt | 1 - Applications/DanceFloor/DanceFloor.lua | 212 - Applications/DanceFloor/Icon.pic | Bin 79 -> 0 bytes Applications/Drone farmer/DroneBIOS.lua | 43 - Applications/Drone farmer/DroneCode.lua | 150 - Applications/Drone farmer/PC.lua | 29 - Applications/DroneGrief/Drone.lua | 139 - Applications/DroneGrief/Planshet.lua | 69 - Applications/EEPROMVirus.lua | 322 -- Applications/{Finder => Finder.app}/Icon.pic | Bin Applications/{Finder => Finder.app}/Main.lua | 198 +- Applications/FlappyBird/Flappy.pic | Bin 274 -> 0 bytes Applications/FlappyBird/FlappyBird.lua | 294 -- Applications/FlappyBird/Icon.pic | Bin 146 -> 0 bytes Applications/FlappyBlock/About.txt | 1 - Applications/FlappyBlock/FlappyBlock.lua | 309 -- Applications/FlappyBlock/Icon.pic | Bin 143 -> 0 bytes Applications/ForceAdmin/About/English.txt | 1 - Applications/ForceAdmin/About/Russian.txt | 1 - Applications/ForceAdmin/ForceAdmin.lua | 93 - Applications/ForceAdmin/Icon.pic | Bin 222 -> 0 bytes .../Icon.pic | Bin .../Main.lua} | 31 +- Applications/GeoScan2/Earth.pic | Bin 1448 -> 0 bytes Applications/GeoScan2/Icon.pic | Bin 206 -> 0 bytes Applications/GeoScan2/Main.lua | 217 - Applications/GitHub/Icon.pic | Bin 191 -> 0 bytes Applications/GitHub/Main.lua | 440 -- Applications/{Graph2 => Graph.app}/Icon.pic | Bin Applications/{Graph2 => Graph.app}/Main.lua | 41 +- Applications/GuessWord/About/English.txt | 1 - Applications/GuessWord/About/Russian.txt | 1 - Applications/GuessWord/GuessWord.lua | 588 --- Applications/GuessWord/Icon.pic | Bin 121 -> 0 bytes Applications/{HEX => HEX.app}/Icon.pic | Bin Applications/{HEX => HEX.app}/Main.lua | 88 +- Applications/HEX/About/English.txt | 1 - Applications/HEX/About/Russian.txt | 1 - Applications/Highlight/Highlight.lua | 13 - Applications/Highlight/Icon.pic | Bin 313 -> 0 bytes Applications/Highlight/TestFile.txt | 23 - .../{HoloClock => HoloClock.app}/Icon.pic | Bin .../HoloClock.lua => HoloClock.app/Main.lua} | 53 +- Applications/HoloEdit/HoloEdit.lua | 785 --- Applications/HoloEdit/Icon.pic | Bin 86 -> 0 bytes .../HoloEdit/Localizations/English.lang | 29 - .../HoloEdit/Localizations/French.lang | 29 - .../HoloEdit/Localizations/Russian.lang | 29 - Applications/{IRC => IRC.app}/Icon.pic | Bin Applications/{IRC => IRC.app}/Main.lua | 111 +- Applications/InfoPanel/About/English.txt | 1 - Applications/InfoPanel/About/Russian.txt | 1 - Applications/InfoPanel/Claims.txt | 15 - Applications/InfoPanel/Icon.pic | Bin 171 -> 0 bytes Applications/InfoPanel/InfoPanel.lua | 127 - Applications/InfoPanel/Main.txt | 21 - Applications/InfoPanel/Rules.txt | 49 - Applications/InfoPanel/SSPI.txt | 35 - Applications/Installer.lua | 263 - Applications/Keyboard/About/English.txt | 1 - Applications/Keyboard/About/Russian.txt | 1 - Applications/Keyboard/Icon.pic | Bin 80 -> 0 bytes Applications/Keyboard/Keyboard.lua | 357 -- .../Icon.pic | Bin .../Localizations}/English.lang | 0 .../Localizations}/French.lang | 0 .../Localizations}/Russian.lang | 0 .../Localizations}/Ukrainian.lang | 0 .../Main.lua | 182 +- Applications/MineSweeper/About/English.txt | 1 - Applications/MineSweeper/About/Russian.txt | 1 - Applications/MineSweeper/Icon.pic | Bin 125 -> 0 bytes Applications/MineSweeper/MineSweeper.lua | 216 - Applications/MultiScreen/MultiScreen.lua | 297 -- Applications/Nano/About/English.txt | 1 - Applications/Nano/About/Russian.txt | 1 - Applications/Nano/Icon.pic | Bin 159 -> 0 bytes Applications/Nano/Nano.lua | 166 - .../Icon.pic | Bin .../Localizations/English.lang | 0 .../Localizations/French.lang | 0 .../Localizations/Russian.lang | 0 .../Localizations/Ukrainian.lang | 0 .../Main.lua | 59 +- .../{Palette => Palette.app}/Icon.pic | Bin Applications/Palette.app/Main.lua | 11 + Applications/Palette/Palette.lua | 11 - Applications/Photoshop/Icon.pic | Bin 161 -> 0 bytes Applications/Photoshop/Photoshop.lua | 490 -- Applications/Photoshop/Tools/1.lua | 160 - Applications/Photoshop/Tools/2.lua | 28 - Applications/Photoshop/Tools/3.lua | 120 - Applications/Photoshop/Tools/4.lua | 40 - Applications/Photoshop/Tools/5.lua | 63 - Applications/Photoshop/Tools/6.lua | 53 - Applications/Photoshop/Tools/7.lua | 52 - Applications/Photoshop/Tools/8.lua | 44 - Applications/Photoshop/Tools/9.lua | 58 - Applications/Piano/Icon.pic | Bin 99 -> 0 bytes Applications/Piano/Piano.lua | 144 - .../Icon.pic | Bin Applications/Picture Edit.app/Main.lua | 501 ++ Applications/Picture Edit.app/Tools/1.lua | 160 + .../Tools/2.lua | 8 +- .../Tools/3.lua | 32 +- .../Tools/4.lua | 18 +- .../Tools/5.lua | 33 +- .../Tools/6.lua | 24 +- Applications/Picture Edit.app/Tools/7.lua | 51 + .../Tools/8.lua | 10 +- .../Tools/9.lua | 23 +- Applications/PictureEdit/Main.lua | 493 -- Applications/PictureEdit/Tools/1.lua | 160 - Applications/PictureEdit/Tools/7.lua | 52 - .../{PrintImage => Print Image.app}/Icon.pic | Bin .../{PrintImage => Print Image.app}/Main.lua | 633 +-- Applications/QuantumCube/About/English.txt | 1 - Applications/QuantumCube/About/Russian.txt | 1 - Applications/QuantumCube/Icon.pic | Bin 97 -> 0 bytes Applications/QuantumCube/QuantumCube.lua | 442 -- Applications/Radio/About/English.txt | 1 - Applications/Radio/About/Russian.txt | 1 - Applications/Radio/Icon.pic | Bin 167 -> 0 bytes Applications/Radio/Radio.lua | 314 -- Applications/RayWalk.app/Icon.pic | Bin 0 -> 101 bytes .../Localizations}/English.lang | 0 .../Localizations}/French.lang | 0 .../Localizations}/Russian.lang | 0 .../{RayWalk => RayWalk.app}/Main.lua | 388 +- Applications/RayWalk.app/RayEngine.cfg | 28 + .../RayWalk.app/RayEngine.lua | 1130 +++-- .../Weapons/CrosshairTextures/Angled.pic | Bin .../Weapons/CrosshairTextures/Default.pic | Bin .../Weapons/CrosshairTextures/Dotted.pic | Bin .../Weapons/CrosshairTextures/Half.pic | Bin .../Weapons/FireTextures/Plasma.pic | Bin .../Weapons/FireTextures/PowderFire.pic | Bin .../Weapons/WeaponTextures/Pistol.pic | Bin .../Weapons/WeaponTextures/Plasmer.pic | Bin .../Weapons/WeaponTextures/Rifle.pic | Bin .../Weapons/WeaponTextures/Sniper.pic | Bin .../Weapons/Weapons.cfg | 0 .../Worlds/ExampleWorld/Blocks.cfg | 0 .../Worlds/ExampleWorld/Map.cfg | 0 .../Worlds/ExampleWorld/Player.cfg | 0 .../Worlds/ExampleWorld/World.cfg | 0 .../Worlds/SundownBeams/Blocks.cfg | 0 .../Worlds/SundownBeams/Map.cfg | 0 .../Worlds/SundownBeams/Player.cfg | 0 .../Worlds/SundownBeams/World.cfg | 0 Applications/RayWalk/Icon.pic | Bin 135 -> 0 bytes Applications/RayWalk/RayEngine.cfg | 28 - Applications/Reinstall OS.app/Icon.pic | Bin 0 -> 185 bytes Applications/Reinstall OS.app/Main.lua | 4 + Applications/Robot/GeoMiner/Full.lua | 319 -- Applications/Robot/GeoMiner/Minified.lua | 1 - Applications/Robot/ReactorFiller.lua | 87 - Applications/Robot/VRScanComputer.lua | 156 - Applications/Robot/VRScanRobot.lua | 133 - Applications/Robot/advancedRobot.lua | 400 -- Applications/Robot/commander.lua | 55 - Applications/Robot/download.lua | 24 - Applications/Robot/experience.lua | 46 - Applications/Robot/fisherEEPROM.lua | 63 - Applications/RobotGriefer/Receiver.lua | 134 - Applications/RobotGriefer/Sender.lua | 114 - Applications/RobotGriefer/untitled.lua | 42 - .../Icon.pic | Bin .../Main.lua} | 43 +- Applications/RunningString/About/English.txt | 1 - Applications/RunningString/About/Russian.txt | 1 - Applications/Sample.app/Icon.pic | Bin 0 -> 130 bytes .../Sample.app/Localizations/English.lang | 3 + .../Sample.app/Localizations/French.lang | 3 + .../Sample.app/Localizations/Russian.lang | 3 + .../Sample.app/Localizations/Ukrainian.lang | 3 + Applications/Sample.app/Main.lua | 46 + Applications/SemyonIC2Reactors.lua | 184 - .../{Settings => Settings.app}/Icon.pic | Bin .../Localizations/English.lang | 18 +- .../Localizations/French.lang | 18 +- .../Localizations/Russian.lang | 16 +- .../Localizations/Ukrainian.lang | 16 +- .../{Settings => Settings.app}/Main.lua | 52 +- .../Modules/0_Screen/Icon.pic | Bin .../Modules/0_Screen/Main.lua | 57 +- .../Modules/1_Wallpaper/Icon.pic | Bin .../Modules/1_Wallpaper/Main.lua | 60 +- .../Modules/2_Icons/Icon.pic | Bin .../Modules/2_Icons/Main.lua | 73 +- .../Modules/3_Tasks/Icon.pic | Bin .../Modules/3_Tasks/Main.lua | 37 +- .../Modules/40_Users}/Icon.pic | Bin .../Settings.app/Modules/40_Users/Main.lua | 227 + .../Modules/4_Disks/Icon.pic | Bin .../Modules/4_Disks/Main.lua | 19 +- .../Modules/5_Network/Icon.pic | Bin .../Modules/5_Network/Main.lua | 55 +- .../Modules/6_Localizations/Icon.pic | Bin .../Modules/6_Localizations/Main.lua | 42 + .../Modules/7_Time/Icon.pic | Bin .../Modules/7_Time/Main.lua | 28 +- .../Modules/8_System/Icon.pic | Bin .../Modules/8_System/Main.lua | 21 +- .../Settings/Modules/00_Users/Main.lua | 72 - .../Settings/Modules/6_Localizations/Main.lua | 44 - Applications/Shooting/Icon.pic | Bin 138 -> 0 bytes Applications/Shooting/Shooting.lua | 286 -- Applications/SmartHouse/Icon.pic | Bin 115 -> 0 bytes .../SmartHouse/Modules/command_block/Icon.pic | Bin 365 -> 0 bytes .../SmartHouse/Modules/command_block/Main.lua | 50 - .../SmartHouse/Modules/homePC/Icon.pic | Bin 299 -> 0 bytes .../SmartHouse/Modules/homePC/Main.lua | 45 - .../SmartHouse/Modules/homePC/Server.pic | Bin 237 -> 0 bytes Applications/SmartHouse/Modules/mfsu/Icon.pic | Bin 295 -> 0 bytes Applications/SmartHouse/Modules/mfsu/Main.lua | 43 - .../SmartHouse/Modules/motion_sensor/Icon.pic | Bin 277 -> 0 bytes .../SmartHouse/Modules/motion_sensor/Main.lua | 53 - .../SmartHouse/Modules/reactor/Icon.pic | Bin 275 -> 0 bytes .../SmartHouse/Modules/reactor/Main.lua | 40 - .../SmartHouse/Modules/redstone/Icon.pic | Bin 359 -> 0 bytes .../SmartHouse/Modules/redstone/Main.lua | 92 - .../SmartHouse/Modules/screen/Icon.pic | Bin 231 -> 0 bytes .../SmartHouse/Modules/screen/Main.lua | 37 - Applications/SmartHouse/SmartHouse.lua | 494 -- Applications/{Spinner => Spinner.app}/1.pic | Bin Applications/{Spinner => Spinner.app}/2.pic | Bin Applications/{Spinner => Spinner.app}/3.pic | Bin Applications/{Spinner => Spinner.app}/4.pic | Bin Applications/{Spinner => Spinner.app}/5.pic | Bin Applications/{Spinner => Spinner.app}/6.pic | Bin Applications/{Spinner => Spinner.app}/7.pic | Bin Applications/{Spinner => Spinner.app}/8.pic | Bin .../{Spinner => Spinner.app}/Icon.pic | Bin .../{Spinner => Spinner.app}/Main.lua | 34 +- Applications/Spinner/About/English.txt | 1 - Applications/Spinner/About/Russian.txt | 1 - .../{Stargate => Stargate.app}/Ch1.pic | Bin .../{Stargate => Stargate.app}/Ch2.pic | Bin .../{Stargate => Stargate.app}/Icon.pic | Bin Applications/Stargate.app/Main.lua | 311 ++ .../{Stargate => Stargate.app}/OffOff.pic | Bin .../{Stargate => Stargate.app}/OffOn.pic | Bin .../{Stargate => Stargate.app}/OnOff.pic | Bin .../{Stargate => Stargate.app}/OnOn.pic | Bin Applications/Stargate/Main.lua | 312 -- .../{Symbols => Symbols.app}/Icon.pic | Bin .../{Symbols => Symbols.app}/Main.lua | 33 +- Applications/Tetris/Icon.pic | Bin 285 -> 0 bytes Applications/Tetris/Tetris.lua | 266 - Applications/Translate.app/Config.cfg | 1 + .../{Translate => Translate.app}/Icon.pic | Bin .../{Translate => Translate.app}/Logo.pic | Bin .../{Translate => Translate.app}/Main.lua | 52 +- Applications/TurretControl/About/English.txt | 1 - Applications/TurretControl/About/Russian.txt | 1 - Applications/TurretControl/Icon.pic | Bin 152 -> 0 bytes Applications/TurretControl/Turret.pic | Bin 324 -> 0 bytes Applications/TurretControl/TurretControl.lua | 291 -- Applications/UniversalInstaller.lua | 147 - Applications/{VK => VK.app}/Icon.pic | Bin .../{VK => VK.app}/Icons/Comments.pic | Bin Applications/{VK => VK.app}/Icons/Likes.pic | Bin Applications/{VK => VK.app}/Icons/Reposts.pic | Bin Applications/{VK => VK.app}/Icons/Views.pic | Bin .../{VK => VK.app}/Localizations/English.lang | 0 .../{VK => VK.app}/Localizations/Russian.lang | 0 Applications/{VK => VK.app}/Main.lua | 191 +- Applications/{VK => VK.app}/Styles/Bright.lua | 0 Applications/{VK => VK.app}/Styles/Dark.lua | 0 .../{VK => VK.app}/Styles/Default.lua | 0 Applications/Viewer/About/English.txt | 1 - Applications/Viewer/About/Russian.txt | 1 - Applications/Viewer/Icon.pic | Bin 245 -> 0 bytes Applications/Viewer/Viewer.lua | 218 - Applications/Viewer/arrowLeft.pic | Bin 155 -> 0 bytes Applications/Viewer/arrowRight.pic | Bin 155 -> 0 bytes Applications/Viewer/play.pic | Bin 161 -> 0 bytes .../{Weather => Weather.app}/Cloudy.pic | Bin .../{Weather => Weather.app}/Foggy.pic | Bin .../{Weather => Weather.app}/Icon.pic | Bin .../Weather.lua => Weather.app/Main.lua} | 55 +- .../{Weather => Weather.app}/Rainy.pic | Bin .../{Weather => Weather.app}/Snowy.pic | Bin .../{Weather => Weather.app}/Stormy.pic | Bin .../{Weather => Weather.app}/Sunny.pic | Bin Applications/archive.lua | 71 - Applications/clear.lua | 8 - Applications/draw.lua | 20 - EFI/Full.lua | 169 +- EFI/Minified.lua | 2 +- Extensions/3dm/ContextMenu.lua | 13 +- Extensions/Arc/Launcher.lua | 4 +- Extensions/Lua/ContextMenu.lua | 39 +- Extensions/Lua/Launcher.lua | 8 +- Extensions/Pic/ContextMenu.lua | 20 +- Files.cfg | 769 --- Icons/Computer.pic | Bin 120 -> 0 bytes Icons/Downloading.pic | Bin 1204 -> 0 bytes Icons/FileNotExists.pic | Bin Icons/Finger.pic | Bin 418 -> 0 bytes Icons/Idiot.pic | Bin 197 -> 0 bytes Icons/Languages.pic | Bin 2492 -> 0 bytes Icons/Love.pic | Bin 984 -> 0 bytes Icons/OK.pic | Bin 598 -> 0 bytes Icons/OS_Logo.pic | Bin 1372 -> 0 bytes Icons/Pastebin.pic | Bin 185 -> 0 bytes Icons/RFID.pic | Bin 224 -> 0 bytes Icons/Robot.pic | Bin 150 -> 0 bytes Icons/SampleIcon.pic | Bin 161 -> 0 bytes Icons/Security.pic | Bin 820 -> 0 bytes Icons/Steve.pic | Bin 195 -> 0 bytes Icons/Tablet.pic | Bin 158 -> 0 bytes Icons/Trash.pic | Bin 132 -> 132 bytes Icons/Update.pic | Bin 622 -> 0 bytes Icons/User.pic | Bin 0 -> 254 bytes Installer.lua | 322 -- Installer/Files.cfg | 229 + Installer/Localizations/English.lang | 22 + Installer/Localizations/French.lang | 22 + Installer/Localizations/Russian.lang | 22 + Installer/Localizations/Ukrainian.lang | 22 + Installer/Main.lua | 609 +++ Installer/Pictures/Done.pic | Bin 0 -> 1064 bytes Installer/Pictures/Downloading.pic | Bin 0 -> 536 bytes Installer/Pictures/EEPROM.pic | Bin 0 -> 1636 bytes Installer/Pictures/HDD.pic | Bin 0 -> 248 bytes Installer/Pictures/Languages.pic | Bin 0 -> 5325 bytes Installer/Pictures/Settings.pic | Bin 0 -> 293 bytes Installer/Pictures/User.pic | Bin 0 -> 254 bytes LICENSE | 2 +- Libraries/.palette.cfg | 1 + lib/archive.lua => Libraries/Archive.lua | 6 +- .../BigLetters.lua | 7 +- Libraries/Bit32.lua | 102 + Libraries/Color.lua | 229 + Libraries/Component.lua | 13 + Libraries/Event.lua | 134 + Libraries/Filesystem.lua | 655 +++ {lib => Libraries}/FormatModules/OCAF.lua | 6 +- Libraries/GUI.lua | 4442 +++++++++++++++++ Libraries/Image.lua | 443 ++ lib/web.lua => Libraries/Internet.lua | 36 +- lib/json.lua => Libraries/JSON.lua | 0 Libraries/Keyboard.lua | 41 + {lib => Libraries}/MeowEngine/Main.lua | 19 +- .../Network.lua | 380 +- Libraries/Number.lua | 36 + {lib => Libraries}/OpenComputersGL/Main.lua | 16 +- .../OpenComputersGL/Materials.lua | 2 +- .../OpenComputersGL/Renderer.lua | 44 +- Libraries/Paths.lua | 57 + Libraries/SHA-256.lua | 202 + Libraries/Screen.lua | 631 +++ Libraries/System.lua | 2578 ++++++++++ Libraries/Text.lua | 232 + lib/vector.lua => Libraries/Vector.lua | 0 Localizations/{OS => }/English.lang | 72 +- Localizations/{OS => }/French.lang | 72 +- Localizations/Installer/English.lang | 10 - Localizations/Installer/French.lang | 10 - Localizations/Installer/Russian.lang | 10 - Localizations/Installer/Ukrainian.lang | 10 - Localizations/{OS => }/Russian.lang | 70 +- Localizations/{OS => }/Ukrainian.lang | 4 +- OS.lua | 740 +-- {Wallpapers => Pictures}/AhsokaTano.pic | Bin {Wallpapers => Pictures}/Block.pic | Bin {Wallpapers => Pictures}/Ciri.pic | Bin {Wallpapers => Pictures}/Girl.pic | Bin {Wallpapers => Pictures}/Mystery.pic | Bin {Wallpapers => Pictures}/Nettle.pic | Bin {Wallpapers => Pictures}/Raspberry.pic | Bin {Wallpapers => Pictures}/Road.pic | Bin {Wallpapers => Pictures}/Space.pic | Bin README.md | 135 +- Screensavers/Clock.lua | 6 +- Screensavers/Lines.lua | 18 +- Screensavers/Mandala.lua | 4 +- Screensavers/Matrix.lua | 6 +- Screensavers/NyanCat.lua | 12 +- Screensavers/XCOM.lua | 14 +- Wallpapers/Catniss.pic | Bin 39107 -> 0 bytes Wallpapers/ChristmasTree.pic | Bin 9843 -> 0 bytes Wallpapers/CloudyEvening.pic | Bin 27499 -> 0 bytes Wallpapers/Field.pic | Bin 39749 -> 0 bytes Wallpapers/HilbertCurve.pic | Bin 8735 -> 0 bytes Wallpapers/IcyLeaf.pic | Bin 39391 -> 0 bytes Wallpapers/MoonTouch.pic | Bin 14131 -> 0 bytes Wallpapers/Mystery2.pic | Bin 30893 -> 0 bytes Wallpapers/Nocturnal.pic | Bin 10077 -> 0 bytes Wallpapers/SnowyBush.pic | Bin 37029 -> 0 bytes Wallpapers/Tatu.pic | Bin 34079 -> 0 bytes Wallpapers/TemplarAssassin.pic | Bin 12485 -> 0 bytes Wallpapers/TyanSunset.pic | Bin 12407 -> 0 bytes Wallpapers/WinterSunrise.pic | Bin 36475 -> 0 bytes etc/profile | 29 - lib/ECSAPI.lua | 2241 --------- lib/FormatModules/OCIF.lua | 201 - lib/FormatModules/RAW.lua | 70 - lib/MineOSCore.lua | 311 -- lib/MineOSInterface.lua | 1513 ------ lib/MineOSPaths.lua | 36 - lib/SHA2.lua | 243 - lib/base64.lua | 39 - lib/context.lua | 137 - lib/crc32lua.lua | 208 - lib/deflatelua.lua | 700 --- lib/doubleHeight.lua | 58 - lib/event.lua | 193 - lib/libPNGImage.lua | 642 --- lib/matrix.lua | 222 - lib/modemConnection.lua | 496 -- lib/multiScreen.lua | 294 -- lib/scale.lua | 40 - lib/serialization.lua | 20 - lib/xmlParser.lua | 59 - 491 files changed, 15872 insertions(+), 28167 deletions(-) rename Applications/{3DPrint => 3D Print.app}/Icon.pic (100%) rename Applications/{3DPrint => 3D Print.app}/Localizations/English.lang (100%) rename Applications/{3DPrint => 3D Print.app}/Localizations/French.lang (100%) rename Applications/{3DPrint => 3D Print.app}/Localizations/Russian.lang (100%) rename Applications/{3DPrint => 3D Print.app}/Localizations/Ukrainian.lang (100%) rename Applications/{3DPrint => 3D Print.app}/Main.lua (87%) rename Applications/{3DTest => 3D Test.app}/Icon.pic (100%) rename Applications/{3DTest/3DTest.lua => 3D Test.app/Main.lua} (64%) mode change 100755 => 100644 delete mode 100644 Applications/3DTest/About/English.txt delete mode 100644 Applications/3DTest/About/Russian.txt create mode 100644 Applications/App Market.app/Icon.pic rename Applications/{AppMarket/Localization => App Market.app/Localizations}/English.lang (98%) rename Applications/{AppMarket/Localization => App Market.app/Localizations}/French.lang (98%) rename Applications/{AppMarket/Localization => App Market.app/Localizations}/Russian.lang (98%) rename Applications/{AppMarket/Localization => App Market.app/Localizations}/Ukrainian.lang (98%) rename Applications/{AppMarket => App Market.app}/Main.lua (63%) delete mode 100644 Applications/AppMarket/Icon.pic delete mode 100644 Applications/AppMarket/Update.pic delete mode 100644 Applications/Battleship/About/English.txt delete mode 100644 Applications/Battleship/About/Russian.txt delete mode 100644 Applications/Battleship/Battleship.lua delete mode 100644 Applications/Battleship/Icon.pic delete mode 100644 Applications/Braille/About/English.txt delete mode 100644 Applications/Braille/About/Russian.txt delete mode 100644 Applications/Braille/Icon.pic delete mode 100644 Applications/Braille/Main.lua delete mode 100644 Applications/BufferDemo/About/English.txt delete mode 100644 Applications/BufferDemo/About/Russian.txt delete mode 100755 Applications/BufferDemo/BufferDemo.lua delete mode 100644 Applications/BufferDemo/Icon.pic delete mode 100644 Applications/BufferDemo/Wallpaper.pic delete mode 100644 Applications/Calc/Calc.lua delete mode 100644 Applications/Calc/Icon.pic rename Applications/{Calculator => Calculator.app}/Icon.pic (100%) rename Applications/{Calculator => Calculator.app}/Main.lua (94%) delete mode 100644 Applications/Calendar/About/English.txt delete mode 100644 Applications/Calendar/About/Russian.txt delete mode 100755 Applications/Calendar/Icon.pic delete mode 100755 Applications/Calendar/Main.lua rename Applications/{Camera => Camera.app}/Icon.pic (100%) rename Applications/{Camera => Camera.app}/Main.lua (82%) delete mode 100644 Applications/Chat/About/English.txt delete mode 100644 Applications/Chat/About/Russian.txt delete mode 100644 Applications/Chat/Chat.lua delete mode 100644 Applications/Chat/Icon.pic delete mode 100644 Applications/Chat/MyAvatar.pic create mode 100755 Applications/Christmas Tree.app/Icon.pic create mode 100755 Applications/Christmas Tree.app/Main.lua delete mode 100644 Applications/ChristmasTree/About/English.txt delete mode 100644 Applications/ChristmasTree/About/Russian.txt delete mode 100644 Applications/ChristmasTree/ChristmasTree.lua delete mode 100644 Applications/ChristmasTree/Icon.pic delete mode 100644 Applications/CodeDoor/About/English.txt delete mode 100644 Applications/CodeDoor/About/Russian.txt delete mode 100644 Applications/CodeDoor/CodeDoor.lua delete mode 100644 Applications/CodeDoor/Icon.pic create mode 100644 Applications/Control.app/Icon.pic rename Applications/{Control/Localization => Control.app/Localizations}/English.lang (100%) rename Applications/{Control/Localization => Control.app/Localizations}/French.lang (100%) rename Applications/{Control/Localization => Control.app/Localizations}/Russian.lang (100%) create mode 100644 Applications/Control.app/Main.lua rename Applications/{Control => Control.app}/Modules/1.lua (79%) rename Applications/{Control => Control.app}/Modules/2.lua (74%) rename Applications/{Control => Control.app}/Modules/3.lua (89%) rename Applications/{Control => Control.app}/Modules/4.lua (75%) delete mode 100644 Applications/Control/Icon.pic delete mode 100644 Applications/Control/Main.lua delete mode 100644 Applications/Crossword/About/English.txt delete mode 100644 Applications/Crossword/About/Russian.txt delete mode 100644 Applications/Crossword/Crossword.lua delete mode 100644 Applications/Crossword/CrosswordFile.txt delete mode 100644 Applications/Crossword/Icon.pic delete mode 100644 Applications/DanceFloor/About/English.txt delete mode 100644 Applications/DanceFloor/About/Russian.txt delete mode 100644 Applications/DanceFloor/DanceFloor.lua delete mode 100644 Applications/DanceFloor/Icon.pic delete mode 100644 Applications/Drone farmer/DroneBIOS.lua delete mode 100644 Applications/Drone farmer/DroneCode.lua delete mode 100644 Applications/Drone farmer/PC.lua delete mode 100644 Applications/DroneGrief/Drone.lua delete mode 100644 Applications/DroneGrief/Planshet.lua delete mode 100644 Applications/EEPROMVirus.lua rename Applications/{Finder => Finder.app}/Icon.pic (100%) rename Applications/{Finder => Finder.app}/Main.lua (70%) delete mode 100644 Applications/FlappyBird/Flappy.pic delete mode 100644 Applications/FlappyBird/FlappyBird.lua delete mode 100644 Applications/FlappyBird/Icon.pic delete mode 100644 Applications/FlappyBlock/About.txt delete mode 100644 Applications/FlappyBlock/FlappyBlock.lua delete mode 100644 Applications/FlappyBlock/Icon.pic delete mode 100644 Applications/ForceAdmin/About/English.txt delete mode 100644 Applications/ForceAdmin/About/Russian.txt delete mode 100644 Applications/ForceAdmin/ForceAdmin.lua delete mode 100644 Applications/ForceAdmin/Icon.pic rename Applications/{FuckTheRain => Fuck the Rain.app}/Icon.pic (100%) rename Applications/{FuckTheRain/FuckTheRain.lua => Fuck the Rain.app/Main.lua} (63%) delete mode 100644 Applications/GeoScan2/Earth.pic delete mode 100644 Applications/GeoScan2/Icon.pic delete mode 100755 Applications/GeoScan2/Main.lua delete mode 100644 Applications/GitHub/Icon.pic delete mode 100644 Applications/GitHub/Main.lua rename Applications/{Graph2 => Graph.app}/Icon.pic (100%) rename Applications/{Graph2 => Graph.app}/Main.lua (82%) delete mode 100644 Applications/GuessWord/About/English.txt delete mode 100644 Applications/GuessWord/About/Russian.txt delete mode 100644 Applications/GuessWord/GuessWord.lua delete mode 100644 Applications/GuessWord/Icon.pic rename Applications/{HEX => HEX.app}/Icon.pic (100%) rename Applications/{HEX => HEX.app}/Main.lua (81%) delete mode 100644 Applications/HEX/About/English.txt delete mode 100644 Applications/HEX/About/Russian.txt delete mode 100644 Applications/Highlight/Highlight.lua delete mode 100644 Applications/Highlight/Icon.pic delete mode 100644 Applications/Highlight/TestFile.txt rename Applications/{HoloClock => HoloClock.app}/Icon.pic (100%) mode change 100644 => 100755 rename Applications/{HoloClock/HoloClock.lua => HoloClock.app/Main.lua} (77%) delete mode 100644 Applications/HoloEdit/HoloEdit.lua delete mode 100644 Applications/HoloEdit/Icon.pic delete mode 100644 Applications/HoloEdit/Localizations/English.lang delete mode 100644 Applications/HoloEdit/Localizations/French.lang delete mode 100644 Applications/HoloEdit/Localizations/Russian.lang rename Applications/{IRC => IRC.app}/Icon.pic (100%) rename Applications/{IRC => IRC.app}/Main.lua (91%) delete mode 100644 Applications/InfoPanel/About/English.txt delete mode 100644 Applications/InfoPanel/About/Russian.txt delete mode 100644 Applications/InfoPanel/Claims.txt delete mode 100644 Applications/InfoPanel/Icon.pic delete mode 100644 Applications/InfoPanel/InfoPanel.lua delete mode 100644 Applications/InfoPanel/Main.txt delete mode 100644 Applications/InfoPanel/Rules.txt delete mode 100644 Applications/InfoPanel/SSPI.txt delete mode 100644 Applications/Installer.lua delete mode 100644 Applications/Keyboard/About/English.txt delete mode 100644 Applications/Keyboard/About/Russian.txt delete mode 100644 Applications/Keyboard/Icon.pic delete mode 100644 Applications/Keyboard/Keyboard.lua rename Applications/{MineCodeIDE => MineCode IDE.app}/Icon.pic (100%) rename Applications/{MineCodeIDE/Localization => MineCode IDE.app/Localizations}/English.lang (100%) rename Applications/{MineCodeIDE/Localization => MineCode IDE.app/Localizations}/French.lang (100%) rename Applications/{MineCodeIDE/Localization => MineCode IDE.app/Localizations}/Russian.lang (100%) rename Applications/{MineCodeIDE/Localization => MineCode IDE.app/Localizations}/Ukrainian.lang (100%) rename Applications/{MineCodeIDE => MineCode IDE.app}/Main.lua (92%) delete mode 100644 Applications/MineSweeper/About/English.txt delete mode 100644 Applications/MineSweeper/About/Russian.txt delete mode 100644 Applications/MineSweeper/Icon.pic delete mode 100644 Applications/MineSweeper/MineSweeper.lua delete mode 100644 Applications/MultiScreen/MultiScreen.lua delete mode 100644 Applications/Nano/About/English.txt delete mode 100644 Applications/Nano/About/Russian.txt delete mode 100644 Applications/Nano/Icon.pic delete mode 100644 Applications/Nano/Nano.lua rename Applications/{Nanomachines => Nanomachines.app}/Icon.pic (100%) rename Applications/{Nanomachines => Nanomachines.app}/Localizations/English.lang (100%) rename Applications/{Nanomachines => Nanomachines.app}/Localizations/French.lang (100%) rename Applications/{Nanomachines => Nanomachines.app}/Localizations/Russian.lang (100%) rename Applications/{Nanomachines => Nanomachines.app}/Localizations/Ukrainian.lang (100%) rename Applications/{Nanomachines => Nanomachines.app}/Main.lua (89%) rename Applications/{Palette => Palette.app}/Icon.pic (100%) create mode 100755 Applications/Palette.app/Main.lua delete mode 100755 Applications/Palette/Palette.lua delete mode 100644 Applications/Photoshop/Icon.pic delete mode 100644 Applications/Photoshop/Photoshop.lua delete mode 100644 Applications/Photoshop/Tools/1.lua delete mode 100644 Applications/Photoshop/Tools/2.lua delete mode 100644 Applications/Photoshop/Tools/3.lua delete mode 100644 Applications/Photoshop/Tools/4.lua delete mode 100644 Applications/Photoshop/Tools/5.lua delete mode 100644 Applications/Photoshop/Tools/6.lua delete mode 100644 Applications/Photoshop/Tools/7.lua delete mode 100644 Applications/Photoshop/Tools/8.lua delete mode 100644 Applications/Photoshop/Tools/9.lua delete mode 100644 Applications/Piano/Icon.pic delete mode 100644 Applications/Piano/Piano.lua rename Applications/{PictureEdit => Picture Edit.app}/Icon.pic (100%) create mode 100644 Applications/Picture Edit.app/Main.lua create mode 100644 Applications/Picture Edit.app/Tools/1.lua rename Applications/{PictureEdit => Picture Edit.app}/Tools/2.lua (71%) rename Applications/{PictureEdit => Picture Edit.app}/Tools/3.lua (78%) rename Applications/{PictureEdit => Picture Edit.app}/Tools/4.lua (61%) rename Applications/{PictureEdit => Picture Edit.app}/Tools/5.lua (64%) rename Applications/{PictureEdit => Picture Edit.app}/Tools/6.lua (67%) create mode 100644 Applications/Picture Edit.app/Tools/7.lua rename Applications/{PictureEdit => Picture Edit.app}/Tools/8.lua (81%) rename Applications/{PictureEdit => Picture Edit.app}/Tools/9.lua (65%) delete mode 100644 Applications/PictureEdit/Main.lua delete mode 100644 Applications/PictureEdit/Tools/1.lua delete mode 100644 Applications/PictureEdit/Tools/7.lua rename Applications/{PrintImage => Print Image.app}/Icon.pic (100%) mode change 100644 => 100755 rename Applications/{PrintImage => Print Image.app}/Main.lua (54%) delete mode 100644 Applications/QuantumCube/About/English.txt delete mode 100644 Applications/QuantumCube/About/Russian.txt delete mode 100644 Applications/QuantumCube/Icon.pic delete mode 100644 Applications/QuantumCube/QuantumCube.lua delete mode 100644 Applications/Radio/About/English.txt delete mode 100644 Applications/Radio/About/Russian.txt delete mode 100644 Applications/Radio/Icon.pic delete mode 100644 Applications/Radio/Radio.lua create mode 100755 Applications/RayWalk.app/Icon.pic rename Applications/{RayWalk/Localization => RayWalk.app/Localizations}/English.lang (100%) rename Applications/{RayWalk/Localization => RayWalk.app/Localizations}/French.lang (100%) rename Applications/{RayWalk/Localization => RayWalk.app/Localizations}/Russian.lang (100%) rename Applications/{RayWalk => RayWalk.app}/Main.lua (87%) create mode 100755 Applications/RayWalk.app/RayEngine.cfg rename lib/rayEngine.lua => Applications/RayWalk.app/RayEngine.lua (87%) rename Applications/{RayWalk => RayWalk.app}/Weapons/CrosshairTextures/Angled.pic (100%) mode change 100644 => 100755 rename Applications/{RayWalk => RayWalk.app}/Weapons/CrosshairTextures/Default.pic (100%) mode change 100644 => 100755 rename Applications/{RayWalk => RayWalk.app}/Weapons/CrosshairTextures/Dotted.pic (100%) mode change 100644 => 100755 rename Applications/{RayWalk => RayWalk.app}/Weapons/CrosshairTextures/Half.pic (100%) mode change 100644 => 100755 rename Applications/{RayWalk => RayWalk.app}/Weapons/FireTextures/Plasma.pic (100%) mode change 100644 => 100755 rename Applications/{RayWalk => RayWalk.app}/Weapons/FireTextures/PowderFire.pic (100%) mode change 100644 => 100755 rename Applications/{RayWalk => RayWalk.app}/Weapons/WeaponTextures/Pistol.pic (100%) mode change 100644 => 100755 rename Applications/{RayWalk => RayWalk.app}/Weapons/WeaponTextures/Plasmer.pic (100%) mode change 100644 => 100755 rename Applications/{RayWalk => RayWalk.app}/Weapons/WeaponTextures/Rifle.pic (100%) mode change 100644 => 100755 rename Applications/{RayWalk => RayWalk.app}/Weapons/WeaponTextures/Sniper.pic (100%) mode change 100644 => 100755 rename Applications/{RayWalk => RayWalk.app}/Weapons/Weapons.cfg (100%) rename Applications/{RayWalk => RayWalk.app}/Worlds/ExampleWorld/Blocks.cfg (100%) rename Applications/{RayWalk => RayWalk.app}/Worlds/ExampleWorld/Map.cfg (100%) rename Applications/{RayWalk => RayWalk.app}/Worlds/ExampleWorld/Player.cfg (100%) rename Applications/{RayWalk => RayWalk.app}/Worlds/ExampleWorld/World.cfg (100%) rename Applications/{RayWalk => RayWalk.app}/Worlds/SundownBeams/Blocks.cfg (100%) rename Applications/{RayWalk => RayWalk.app}/Worlds/SundownBeams/Map.cfg (100%) rename Applications/{RayWalk => RayWalk.app}/Worlds/SundownBeams/Player.cfg (100%) rename Applications/{RayWalk => RayWalk.app}/Worlds/SundownBeams/World.cfg (100%) delete mode 100644 Applications/RayWalk/Icon.pic delete mode 100755 Applications/RayWalk/RayEngine.cfg create mode 100644 Applications/Reinstall OS.app/Icon.pic create mode 100644 Applications/Reinstall OS.app/Main.lua delete mode 100644 Applications/Robot/GeoMiner/Full.lua delete mode 100644 Applications/Robot/GeoMiner/Minified.lua delete mode 100644 Applications/Robot/ReactorFiller.lua delete mode 100644 Applications/Robot/VRScanComputer.lua delete mode 100644 Applications/Robot/VRScanRobot.lua delete mode 100644 Applications/Robot/advancedRobot.lua delete mode 100644 Applications/Robot/commander.lua delete mode 100644 Applications/Robot/download.lua delete mode 100644 Applications/Robot/experience.lua delete mode 100644 Applications/Robot/fisherEEPROM.lua delete mode 100644 Applications/RobotGriefer/Receiver.lua delete mode 100644 Applications/RobotGriefer/Sender.lua delete mode 100644 Applications/RobotGriefer/untitled.lua rename Applications/{RunningString => Running String.app}/Icon.pic (100%) mode change 100755 => 100644 rename Applications/{RunningString/RunningString.lua => Running String.app/Main.lua} (70%) delete mode 100644 Applications/RunningString/About/English.txt delete mode 100644 Applications/RunningString/About/Russian.txt create mode 100644 Applications/Sample.app/Icon.pic create mode 100644 Applications/Sample.app/Localizations/English.lang create mode 100644 Applications/Sample.app/Localizations/French.lang create mode 100644 Applications/Sample.app/Localizations/Russian.lang create mode 100644 Applications/Sample.app/Localizations/Ukrainian.lang create mode 100644 Applications/Sample.app/Main.lua delete mode 100644 Applications/SemyonIC2Reactors.lua rename Applications/{Settings => Settings.app}/Icon.pic (100%) rename Applications/{Settings => Settings.app}/Localizations/English.lang (84%) rename Applications/{Settings => Settings.app}/Localizations/French.lang (84%) rename Applications/{Settings => Settings.app}/Localizations/Russian.lang (85%) rename Applications/{Settings => Settings.app}/Localizations/Ukrainian.lang (85%) rename Applications/{Settings => Settings.app}/Main.lua (72%) rename Applications/{Settings => Settings.app}/Modules/0_Screen/Icon.pic (100%) rename Applications/{Settings => Settings.app}/Modules/0_Screen/Main.lua (67%) rename Applications/{Settings => Settings.app}/Modules/1_Wallpaper/Icon.pic (100%) rename Applications/{Settings => Settings.app}/Modules/1_Wallpaper/Main.lua (52%) rename Applications/{Settings => Settings.app}/Modules/2_Icons/Icon.pic (100%) rename Applications/{Settings => Settings.app}/Modules/2_Icons/Main.lua (51%) rename Applications/{Settings => Settings.app}/Modules/3_Tasks/Icon.pic (100%) rename Applications/{Settings => Settings.app}/Modules/3_Tasks/Main.lua (66%) rename Applications/{Settings/Modules/00_Users => Settings.app/Modules/40_Users}/Icon.pic (100%) create mode 100644 Applications/Settings.app/Modules/40_Users/Main.lua rename Applications/{Settings => Settings.app}/Modules/4_Disks/Icon.pic (100%) rename Applications/{Settings => Settings.app}/Modules/4_Disks/Main.lua (88%) rename Applications/{Settings => Settings.app}/Modules/5_Network/Icon.pic (100%) rename Applications/{Settings => Settings.app}/Modules/5_Network/Main.lua (60%) rename Applications/{Settings => Settings.app}/Modules/6_Localizations/Icon.pic (100%) create mode 100644 Applications/Settings.app/Modules/6_Localizations/Main.lua rename Applications/{Settings => Settings.app}/Modules/7_Time/Icon.pic (100%) rename Applications/{Settings => Settings.app}/Modules/7_Time/Main.lua (61%) rename Applications/{Settings => Settings.app}/Modules/8_System/Icon.pic (100%) rename Applications/{Settings => Settings.app}/Modules/8_System/Main.lua (77%) delete mode 100644 Applications/Settings/Modules/00_Users/Main.lua delete mode 100644 Applications/Settings/Modules/6_Localizations/Main.lua delete mode 100644 Applications/Shooting/Icon.pic delete mode 100644 Applications/Shooting/Shooting.lua delete mode 100644 Applications/SmartHouse/Icon.pic delete mode 100644 Applications/SmartHouse/Modules/command_block/Icon.pic delete mode 100755 Applications/SmartHouse/Modules/command_block/Main.lua delete mode 100644 Applications/SmartHouse/Modules/homePC/Icon.pic delete mode 100755 Applications/SmartHouse/Modules/homePC/Main.lua delete mode 100644 Applications/SmartHouse/Modules/homePC/Server.pic delete mode 100644 Applications/SmartHouse/Modules/mfsu/Icon.pic delete mode 100755 Applications/SmartHouse/Modules/mfsu/Main.lua delete mode 100644 Applications/SmartHouse/Modules/motion_sensor/Icon.pic delete mode 100755 Applications/SmartHouse/Modules/motion_sensor/Main.lua delete mode 100644 Applications/SmartHouse/Modules/reactor/Icon.pic delete mode 100755 Applications/SmartHouse/Modules/reactor/Main.lua delete mode 100644 Applications/SmartHouse/Modules/redstone/Icon.pic delete mode 100755 Applications/SmartHouse/Modules/redstone/Main.lua delete mode 100644 Applications/SmartHouse/Modules/screen/Icon.pic delete mode 100755 Applications/SmartHouse/Modules/screen/Main.lua delete mode 100755 Applications/SmartHouse/SmartHouse.lua rename Applications/{Spinner => Spinner.app}/1.pic (100%) rename Applications/{Spinner => Spinner.app}/2.pic (100%) rename Applications/{Spinner => Spinner.app}/3.pic (100%) rename Applications/{Spinner => Spinner.app}/4.pic (100%) rename Applications/{Spinner => Spinner.app}/5.pic (100%) rename Applications/{Spinner => Spinner.app}/6.pic (100%) rename Applications/{Spinner => Spinner.app}/7.pic (100%) rename Applications/{Spinner => Spinner.app}/8.pic (100%) rename Applications/{Spinner => Spinner.app}/Icon.pic (100%) rename Applications/{Spinner => Spinner.app}/Main.lua (70%) delete mode 100644 Applications/Spinner/About/English.txt delete mode 100755 Applications/Spinner/About/Russian.txt rename Applications/{Stargate => Stargate.app}/Ch1.pic (100%) mode change 100644 => 100755 rename Applications/{Stargate => Stargate.app}/Ch2.pic (100%) mode change 100644 => 100755 rename Applications/{Stargate => Stargate.app}/Icon.pic (100%) mode change 100644 => 100755 create mode 100755 Applications/Stargate.app/Main.lua rename Applications/{Stargate => Stargate.app}/OffOff.pic (100%) mode change 100644 => 100755 rename Applications/{Stargate => Stargate.app}/OffOn.pic (100%) mode change 100644 => 100755 rename Applications/{Stargate => Stargate.app}/OnOff.pic (100%) mode change 100644 => 100755 rename Applications/{Stargate => Stargate.app}/OnOn.pic (100%) mode change 100644 => 100755 delete mode 100755 Applications/Stargate/Main.lua rename Applications/{Symbols => Symbols.app}/Icon.pic (100%) rename Applications/{Symbols => Symbols.app}/Main.lua (89%) delete mode 100644 Applications/Tetris/Icon.pic delete mode 100644 Applications/Tetris/Tetris.lua create mode 100644 Applications/Translate.app/Config.cfg rename Applications/{Translate => Translate.app}/Icon.pic (100%) rename Applications/{Translate => Translate.app}/Logo.pic (100%) rename Applications/{Translate => Translate.app}/Main.lua (82%) delete mode 100644 Applications/TurretControl/About/English.txt delete mode 100644 Applications/TurretControl/About/Russian.txt delete mode 100644 Applications/TurretControl/Icon.pic delete mode 100644 Applications/TurretControl/Turret.pic delete mode 100644 Applications/TurretControl/TurretControl.lua delete mode 100644 Applications/UniversalInstaller.lua rename Applications/{VK => VK.app}/Icon.pic (100%) rename Applications/{VK => VK.app}/Icons/Comments.pic (100%) rename Applications/{VK => VK.app}/Icons/Likes.pic (100%) rename Applications/{VK => VK.app}/Icons/Reposts.pic (100%) rename Applications/{VK => VK.app}/Icons/Views.pic (100%) rename Applications/{VK => VK.app}/Localizations/English.lang (100%) rename Applications/{VK => VK.app}/Localizations/Russian.lang (100%) rename Applications/{VK => VK.app}/Main.lua (92%) rename Applications/{VK => VK.app}/Styles/Bright.lua (100%) rename Applications/{VK => VK.app}/Styles/Dark.lua (100%) rename Applications/{VK => VK.app}/Styles/Default.lua (100%) delete mode 100644 Applications/Viewer/About/English.txt delete mode 100644 Applications/Viewer/About/Russian.txt delete mode 100644 Applications/Viewer/Icon.pic delete mode 100755 Applications/Viewer/Viewer.lua delete mode 100644 Applications/Viewer/arrowLeft.pic delete mode 100644 Applications/Viewer/arrowRight.pic delete mode 100644 Applications/Viewer/play.pic rename Applications/{Weather => Weather.app}/Cloudy.pic (100%) rename Applications/{Weather => Weather.app}/Foggy.pic (100%) rename Applications/{Weather => Weather.app}/Icon.pic (100%) rename Applications/{Weather/Weather.lua => Weather.app/Main.lua} (66%) rename Applications/{Weather => Weather.app}/Rainy.pic (100%) rename Applications/{Weather => Weather.app}/Snowy.pic (100%) rename Applications/{Weather => Weather.app}/Stormy.pic (100%) rename Applications/{Weather => Weather.app}/Sunny.pic (100%) delete mode 100644 Applications/archive.lua delete mode 100644 Applications/clear.lua delete mode 100644 Applications/draw.lua delete mode 100644 Files.cfg delete mode 100644 Icons/Computer.pic delete mode 100644 Icons/Downloading.pic mode change 100644 => 100755 Icons/FileNotExists.pic delete mode 100644 Icons/Finger.pic delete mode 100644 Icons/Idiot.pic delete mode 100644 Icons/Languages.pic delete mode 100644 Icons/Love.pic delete mode 100644 Icons/OK.pic delete mode 100644 Icons/OS_Logo.pic delete mode 100644 Icons/Pastebin.pic delete mode 100644 Icons/RFID.pic delete mode 100644 Icons/Robot.pic delete mode 100644 Icons/SampleIcon.pic delete mode 100644 Icons/Security.pic delete mode 100644 Icons/Steve.pic delete mode 100644 Icons/Tablet.pic mode change 100644 => 100755 Icons/Trash.pic delete mode 100644 Icons/Update.pic create mode 100644 Icons/User.pic delete mode 100644 Installer.lua create mode 100644 Installer/Files.cfg create mode 100644 Installer/Localizations/English.lang create mode 100644 Installer/Localizations/French.lang create mode 100644 Installer/Localizations/Russian.lang create mode 100644 Installer/Localizations/Ukrainian.lang create mode 100644 Installer/Main.lua create mode 100644 Installer/Pictures/Done.pic create mode 100644 Installer/Pictures/Downloading.pic create mode 100644 Installer/Pictures/EEPROM.pic create mode 100644 Installer/Pictures/HDD.pic create mode 100644 Installer/Pictures/Languages.pic create mode 100644 Installer/Pictures/Settings.pic create mode 100644 Installer/Pictures/User.pic create mode 100644 Libraries/.palette.cfg rename lib/archive.lua => Libraries/Archive.lua (95%) rename lib/bigLetters.lua => Libraries/BigLetters.lua (98%) create mode 100755 Libraries/Bit32.lua create mode 100755 Libraries/Color.lua create mode 100644 Libraries/Component.lua create mode 100755 Libraries/Event.lua create mode 100644 Libraries/Filesystem.lua rename {lib => Libraries}/FormatModules/OCAF.lua (98%) create mode 100755 Libraries/GUI.lua create mode 100755 Libraries/Image.lua rename lib/web.lua => Libraries/Internet.lua (76%) rename lib/json.lua => Libraries/JSON.lua (100%) mode change 100644 => 100755 create mode 100755 Libraries/Keyboard.lua rename {lib => Libraries}/MeowEngine/Main.lua (98%) rename lib/MineOSNetwork.lua => Libraries/Network.lua (57%) create mode 100755 Libraries/Number.lua rename {lib => Libraries}/OpenComputersGL/Main.lua (97%) rename {lib => Libraries}/OpenComputersGL/Materials.lua (97%) rename {lib => Libraries}/OpenComputersGL/Renderer.lua (92%) create mode 100755 Libraries/Paths.lua create mode 100755 Libraries/SHA-256.lua create mode 100755 Libraries/Screen.lua create mode 100755 Libraries/System.lua create mode 100755 Libraries/Text.lua rename lib/vector.lua => Libraries/Vector.lua (100%) rename Localizations/{OS => }/English.lang (52%) rename Localizations/{OS => }/French.lang (51%) delete mode 100644 Localizations/Installer/English.lang delete mode 100644 Localizations/Installer/French.lang delete mode 100644 Localizations/Installer/Russian.lang delete mode 100644 Localizations/Installer/Ukrainian.lang rename Localizations/{OS => }/Russian.lang (53%) rename Localizations/{OS => }/Ukrainian.lang (97%) mode change 100755 => 100644 OS.lua rename {Wallpapers => Pictures}/AhsokaTano.pic (100%) mode change 100644 => 100755 rename {Wallpapers => Pictures}/Block.pic (100%) mode change 100644 => 100755 rename {Wallpapers => Pictures}/Ciri.pic (100%) mode change 100644 => 100755 rename {Wallpapers => Pictures}/Girl.pic (100%) mode change 100644 => 100755 rename {Wallpapers => Pictures}/Mystery.pic (100%) mode change 100644 => 100755 rename {Wallpapers => Pictures}/Nettle.pic (100%) mode change 100644 => 100755 rename {Wallpapers => Pictures}/Raspberry.pic (100%) mode change 100644 => 100755 rename {Wallpapers => Pictures}/Road.pic (100%) rename {Wallpapers => Pictures}/Space.pic (100%) mode change 100644 => 100755 mode change 100644 => 100755 Screensavers/Clock.lua mode change 100644 => 100755 Screensavers/Mandala.lua mode change 100644 => 100755 Screensavers/Matrix.lua delete mode 100644 Wallpapers/Catniss.pic delete mode 100644 Wallpapers/ChristmasTree.pic delete mode 100644 Wallpapers/CloudyEvening.pic delete mode 100644 Wallpapers/Field.pic delete mode 100644 Wallpapers/HilbertCurve.pic delete mode 100644 Wallpapers/IcyLeaf.pic delete mode 100644 Wallpapers/MoonTouch.pic delete mode 100644 Wallpapers/Mystery2.pic delete mode 100644 Wallpapers/Nocturnal.pic delete mode 100644 Wallpapers/SnowyBush.pic delete mode 100644 Wallpapers/Tatu.pic delete mode 100644 Wallpapers/TemplarAssassin.pic delete mode 100644 Wallpapers/TyanSunset.pic delete mode 100644 Wallpapers/WinterSunrise.pic delete mode 100644 etc/profile delete mode 100755 lib/ECSAPI.lua delete mode 100755 lib/FormatModules/OCIF.lua delete mode 100755 lib/FormatModules/RAW.lua delete mode 100755 lib/MineOSCore.lua delete mode 100755 lib/MineOSInterface.lua delete mode 100755 lib/MineOSPaths.lua delete mode 100644 lib/SHA2.lua delete mode 100755 lib/base64.lua delete mode 100644 lib/context.lua delete mode 100644 lib/crc32lua.lua delete mode 100644 lib/deflatelua.lua delete mode 100755 lib/doubleHeight.lua delete mode 100755 lib/event.lua delete mode 100644 lib/libPNGImage.lua delete mode 100644 lib/matrix.lua delete mode 100644 lib/modemConnection.lua delete mode 100644 lib/multiScreen.lua delete mode 100755 lib/scale.lua delete mode 100755 lib/serialization.lua delete mode 100644 lib/xmlParser.lua diff --git a/.gitignore b/.gitignore index 496ee2ca..382f1c84 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -.DS_Store \ No newline at end of file +.DS_Store +Mounts/ +Temporary/ +Users/ \ No newline at end of file diff --git a/Applications/3DPrint/Icon.pic b/Applications/3D Print.app/Icon.pic similarity index 100% rename from Applications/3DPrint/Icon.pic rename to Applications/3D Print.app/Icon.pic diff --git a/Applications/3DPrint/Localizations/English.lang b/Applications/3D Print.app/Localizations/English.lang similarity index 100% rename from Applications/3DPrint/Localizations/English.lang rename to Applications/3D Print.app/Localizations/English.lang diff --git a/Applications/3DPrint/Localizations/French.lang b/Applications/3D Print.app/Localizations/French.lang similarity index 100% rename from Applications/3DPrint/Localizations/French.lang rename to Applications/3D Print.app/Localizations/French.lang diff --git a/Applications/3DPrint/Localizations/Russian.lang b/Applications/3D Print.app/Localizations/Russian.lang similarity index 100% rename from Applications/3DPrint/Localizations/Russian.lang rename to Applications/3D Print.app/Localizations/Russian.lang diff --git a/Applications/3DPrint/Localizations/Ukrainian.lang b/Applications/3D Print.app/Localizations/Ukrainian.lang similarity index 100% rename from Applications/3DPrint/Localizations/Ukrainian.lang rename to Applications/3D Print.app/Localizations/Ukrainian.lang diff --git a/Applications/3DPrint/Main.lua b/Applications/3D Print.app/Main.lua similarity index 87% rename from Applications/3DPrint/Main.lua rename to Applications/3D Print.app/Main.lua index b68a7fc4..09e40fb7 100644 --- a/Applications/3DPrint/Main.lua +++ b/Applications/3D Print.app/Main.lua @@ -1,18 +1,16 @@ -local args, options = require("shell").parse(...) - -require("advancedLua") -local component = require("component") +local number = require("Number") local GUI = require("GUI") -local buffer = require("doubleBuffering") -local color = require("color") -local unicode = require("unicode") -local MineOSCore = require("MineOSCore") +local screen = require("Screen") +local color = require("Color") +local system = require("System") local bigLetters = require("bigLetters") +local args, options = system.parseArguments(...) + -------------------------------------------------------------------------------- -local localization = MineOSCore.getCurrentScriptLocalization() +local localization = system.getCurrentScriptLocalization() local currentLayer = 0 local model local shapeLimit = 24 @@ -27,17 +25,17 @@ end -------------------------------------------------------------------------------- -local application = GUI.application() +local workspace = GUI.workspace() -local toolPanel = application:addChild(GUI.panel(1, 1, 28, application.height, 0x2D2D2D)) -local toolLayout = application:addChild(GUI.layout(1, 1, toolPanel.width, toolPanel.height - 3, 1, 1)) +local toolPanel = workspace:addChild(GUI.panel(1, 1, 28, workspace.height, 0x2D2D2D)) +local toolLayout = workspace:addChild(GUI.layout(1, 1, toolPanel.width, toolPanel.height - 3, 1, 1)) toolLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) toolLayout:setMargin(1, 1, 0, 1) local function addSeparator(text) toolLayout:addChild(GUI.object(1, 1, toolLayout.width, 1)).draw = function(object) - buffer.drawRectangle(object.x, object.y, object.width, 1, 0x0F0F0F, 0xE1E1E1, " ") - buffer.drawText(object.x + 1, object.y, 0xE1E1E1, text) + screen.drawRectangle(object.x, object.y, object.width, 1, 0x0F0F0F, 0xE1E1E1, " ") + screen.drawText(object.x + 1, object.y, 0xE1E1E1, text) end end @@ -49,7 +47,7 @@ local function addColorSelector(...) return toolLayout:addChild(GUI.colorSelector(1, 1, toolLayout.width - 2, 1, ...)) end -local printButton = application:addChild(GUI.button(1, application.height - 2, toolLayout.width, 3, 0x4B4B4B, 0xD2D2D2, 0xE1E1E1, 0x3C3C3C, localization.print)) +local printButton = workspace:addChild(GUI.button(1, workspace.height - 2, toolLayout.width, 3, 0x4B4B4B, 0xD2D2D2, 0xE1E1E1, 0x3C3C3C, localization.print)) toolLayout:addChild(GUI.object(1, 1, toolLayout.width, 5)).draw = function(object) local text = tostring(math.floor(currentLayer)) @@ -63,11 +61,11 @@ local newButton = addButton(localization.new) local openButton = addButton(localization.open) addButton(localization.save).onTouch = function() - local filesystemDialog = GUI.addFilesystemDialog(application, true, 50, math.floor(application.height * 0.8), "Save", "Cancel", "File name", "/") + local filesystemDialog = GUI.addFilesystemDialog(workspace, true, 50, math.floor(workspace.height * 0.8), "Save", "Cancel", "File name", "/") filesystemDialog:setMode(GUI.IO_MODE_SAVE, GUI.IO_MODE_FILE) filesystemDialog:addExtensionFilter(".3dm") filesystemDialog.onSubmit = function(path) - table.toFile(path, model, true) + filesystem.writeTable(path, model, true) end filesystemDialog:show() end @@ -77,7 +75,7 @@ addButton(localization.exit).onTouch = function() hologram.clear() end - application:stop() + workspace:stop() end addSeparator(localization.elementSettings) @@ -170,7 +168,7 @@ if proxies.hologram then selector.onColorSelected = function() if proxies.hologram then proxies.hologram.setPaletteColor(i, selector.color) - application:draw() + workspace:draw() end end end @@ -261,7 +259,7 @@ local function updateModelFromWidgets() end local function load(path) - model = table.fromFile(path) + model = filesystem.readTable(path) updateComboBoxFromModel() updateWidgetsFromModel() @@ -269,28 +267,28 @@ local function load(path) end openButton.onTouch = function() - local filesystemDialog = GUI.addFilesystemDialog(application, true, 50, math.floor(application.height * 0.8), "Open", "Cancel", "File name", "/") + local filesystemDialog = GUI.addFilesystemDialog(workspace, true, 50, math.floor(workspace.height * 0.8), "Open", "Cancel", "File name", "/") filesystemDialog:setMode(GUI.IO_MODE_OPEN, GUI.IO_MODE_FILE) filesystemDialog:addExtensionFilter(".3dm") filesystemDialog.onSubmit = function(path) load(path) - application:draw() + workspace:draw() updateOnHologram() end filesystemDialog:show() end -application:addChild(GUI.panel(toolPanel.width + 1, 1, application.width - toolPanel.width, toolPanel.height, 0x1E1E1E)) +workspace:addChild(GUI.panel(toolPanel.width + 1, 1, workspace.width - toolPanel.width, toolPanel.height, 0x1E1E1E)) -local view = application:addChild(GUI.object(1, 1, 16 * viewPixelWidth, 16 * viewPixelHeight)) -view.localX = math.floor(toolLayout.width + (application.width - toolLayout.width) / 2 - view.width / 2) -view.localY = math.floor(application.height / 2 - view.height / 2) +local view = workspace:addChild(GUI.object(1, 1, 16 * viewPixelWidth, 16 * viewPixelHeight)) +view.localX = math.floor(toolLayout.width + (workspace.width - toolLayout.width) / 2 - view.width / 2) +view.localY = math.floor(workspace.height / 2 - view.height / 2) view.draw = function() local x, y, step = view.x, view.y, true for j = 1, 16 do for i = 1, 16 do - buffer.drawRectangle(x, y, viewPixelWidth, viewPixelHeight, 0xF0F0F0, 0xE1E1E1, step and " " or "█") + screen.drawRectangle(x, y, viewPixelWidth, viewPixelHeight, 0xF0F0F0, 0xE1E1E1, step and " " or "█") x, step = x + viewPixelWidth, not step end @@ -311,14 +309,14 @@ view.draw = function() local y = view.y + view.height - shape[2] * viewPixelHeight - height if width > 0 and height > 0 and currentLayer >= shape[3] and currentLayer <= shape[6] - 1 then - buffer.drawRectangle(x, y, width, height, i == shapeIndex and colors[i] or color.blend(colors[i], 0xFFFFFF, 0.5), 0x0, " ") + screen.drawRectangle(x, y, width, height, i == shapeIndex and colors[i] or color.blend(colors[i], 0xFFFFFF, 0.5), 0x0, " ") if currentLayer == shape[3] then - buffer.drawRectangle(x, y, viewPixelWidth, viewPixelHeight, 0x0, 0x0, " ", i == shapeIndex and 0.2 or 0.6) + screen.drawRectangle(x, y, viewPixelWidth, viewPixelHeight, 0x0, 0x0, " ", i == shapeIndex and 0.2 or 0.6) end if currentLayer == shape[6] - 1 then - buffer.drawRectangle(x + width - viewPixelWidth, y + height - viewPixelHeight, viewPixelWidth, viewPixelHeight, 0x0, 0x0, " ", i == shapeIndex and 0.4 or 0.8) + screen.drawRectangle(x + width - viewPixelWidth, y + height - viewPixelHeight, viewPixelWidth, viewPixelHeight, 0x0, 0x0, " ", i == shapeIndex and 0.4 or 0.8) end end end @@ -326,26 +324,26 @@ view.draw = function() end end -toolLayout.eventHandler = function(application, toolLayout, e1, e2, e3, e4, e5) +toolLayout.eventHandler = function(workspace, toolLayout, e1, e2, e3, e4, e5) if e1 == "scroll" then local cell = toolLayout.cells[1][1] if e5 > 0 then if cell.verticalMargin < 1 then cell.verticalMargin = cell.verticalMargin + 1 - application:draw() + workspace:draw() end else local child = toolLayout.children[#toolLayout.children] if child.localY + child.height - 1 >= toolLayout.localY + toolLayout.height - 1 then cell.verticalMargin = cell.verticalMargin - 1 - application:draw() + workspace:draw() end end end end local touchX, touchY, shapeX, shapeY, shapeZ -view.eventHandler = function(application, view, e1, e2, e3, e4, e5) +view.eventHandler = function(workspace, view, e1, e2, e3, e4, e5) if e1 == "touch" or e1 == "drag" then if e5 > 0 then if e1 == "touch" then @@ -354,7 +352,7 @@ view.eventHandler = function(application, view, e1, e2, e3, e4, e5) view.localX, view.localY = view.localX + e3 - touchX, view.localY + e4 - touchY touchX, touchY = e3, e4 - application:draw() + workspace:draw() end else local shapeIndex = getCurrentShapeIndex() @@ -374,7 +372,7 @@ view.eventHandler = function(application, view, e1, e2, e3, e4, e5) shape[4], shape[5], shape[6] = shape[4] + 1, shape[5] + 1, shape[6] + 1 end - application:draw() + workspace:draw() end end elseif e1 == "drop" then @@ -397,7 +395,7 @@ view.eventHandler = function(application, view, e1, e2, e3, e4, e5) currentLayer = currentLayer + 1 fix() - application:draw() + workspace:draw() updateOnHologram() end else @@ -405,7 +403,7 @@ view.eventHandler = function(application, view, e1, e2, e3, e4, e5) currentLayer = currentLayer - 1 fix() - application:draw() + workspace:draw() updateOnHologram() end end @@ -430,7 +428,7 @@ rotateButton.onTouch = function() fixShape(shape) end - application:draw() + workspace:draw() updateOnHologram() end @@ -453,7 +451,7 @@ flipButton.onTouch = function() fixShape(shape) end - application:draw() + workspace:draw() updateOnHologram() end @@ -462,7 +460,7 @@ disabledListItem.onTouch = function() updateWidgetsFromModel() updateAddRemoveButtonsState() - application:draw() + workspace:draw() updateOnHologram() end @@ -485,14 +483,14 @@ end newButton.onTouch = function() new() - application:draw() + workspace:draw() updateOnHologram() end addShapeButton.onTouch = function() addShape() - application:draw() + workspace:draw() updateOnHologram() end @@ -503,7 +501,7 @@ removeShapeButton.onTouch = function() updateWidgetsFromModel() updateAddRemoveButtonsState() - application:draw() + workspace:draw() updateOnHologram() end @@ -548,7 +546,7 @@ end elementComboBox.onItemSelected = function() updateWidgetsFromModel() - application:draw() + workspace:draw() updateOnHologram() end @@ -570,6 +568,6 @@ else new() end -application:draw() +workspace:draw() updateOnHologram() -application:start() \ No newline at end of file +workspace:start() \ No newline at end of file diff --git a/Applications/3DTest/Icon.pic b/Applications/3D Test.app/Icon.pic similarity index 100% rename from Applications/3DTest/Icon.pic rename to Applications/3D Test.app/Icon.pic diff --git a/Applications/3DTest/3DTest.lua b/Applications/3D Test.app/Main.lua old mode 100755 new mode 100644 similarity index 64% rename from Applications/3DTest/3DTest.lua rename to Applications/3D Test.app/Main.lua index 42d8ef07..8aa522a2 --- a/Applications/3DTest/3DTest.lua +++ b/Applications/3D Test.app/Main.lua @@ -1,20 +1,11 @@ -------------------------------------------------------- Libraries -------------------------------------------------------- --- package.loaded["GUI"] = nil --- package.loaded["doubleBuffering"] = nil --- package.loaded["vector"] = nil --- package.loaded["OpenComputersGL/Main"] = nil --- package.loaded["OpenComputersGL/Materials"] = nil --- package.loaded["OpenComputersGL/Renderer"] = nil --- package.loaded["MeowEngine/Main"] = nil - -local color = require("color") -local computer = require("computer") -local buffer = require("doubleBuffering") -local event = require("event") +local color = require("Color") +local screen = require("Screen") +local event = require("Event") local GUI = require("GUI") -local vector = require("vector") +local vector = require("Vector") local materials = require("OpenComputersGL/Materials") local renderer = require("OpenComputersGL/Renderer") local OCGL = require("OpenComputersGL/Main") @@ -24,10 +15,10 @@ local meowEngine = require("MeowEngine/Main") -- /MineOS/Desktop/3DTest.app/3DTest.lua -buffer.flush() +screen.flush() meowEngine.intro(vector.newVector3(0, 0, 0), 20) -local application = GUI.application() +local workspace = GUI.workspace() local scene = meowEngine.newScene(0x1D1D1D) scene.renderMode = OCGL.renderModes.flatShading @@ -280,14 +271,14 @@ local function move(x, y, z) end local function moveLight(x, y, z) - scene.lights[application.toolbar.lightSelectComboBox.selectedItem].position[1] = scene.lights[application.toolbar.lightSelectComboBox.selectedItem].position[1] + x - scene.lights[application.toolbar.lightSelectComboBox.selectedItem].position[2] = scene.lights[application.toolbar.lightSelectComboBox.selectedItem].position[2] + y - scene.lights[application.toolbar.lightSelectComboBox.selectedItem].position[3] = scene.lights[application.toolbar.lightSelectComboBox.selectedItem].position[3] + z + scene.lights[workspace.toolbar.lightSelectComboBox.selectedItem].position[1] = scene.lights[workspace.toolbar.lightSelectComboBox.selectedItem].position[1] + x + scene.lights[workspace.toolbar.lightSelectComboBox.selectedItem].position[2] = scene.lights[workspace.toolbar.lightSelectComboBox.selectedItem].position[2] + y + scene.lights[workspace.toolbar.lightSelectComboBox.selectedItem].position[3] = scene.lights[workspace.toolbar.lightSelectComboBox.selectedItem].position[3] + z end local controls = { -- F1 - [59 ] = function() application.toolbar.hidden = not application.toolbar.hidden; application.infoTextBox.hidden = not application.infoTextBox.hidden end, + [59 ] = function() workspace.toolbar.hidden = not workspace.toolbar.hidden; workspace.infoTextBox.hidden = not workspace.infoTextBox.hidden end, -- Arrows [200] = function() scene.camera:rotate(-rotationAngle, 0, 0) end, [208] = function() scene.camera:rotate(rotationAngle, 0, 0) end, @@ -314,12 +305,12 @@ local controls = { -------------------------------------------------------- GUI -------------------------------------------------------- -local OCGLView = GUI.object(1, 1, application.width, application.height) +local OCGLView = GUI.object(1, 1, workspace.width, workspace.height) local function drawInvertedText(x, y, text) - local index = buffer.getIndex(x, y) - local background, foreground = buffer.rawGet(index) - buffer.rawSet(index, background, 0xFFFFFF - foreground, text) + local index = screen.getIndex(x, y) + local background, foreground = screen.rawGet(index) + screen.rawSet(index, background, 0xFFFFFF - foreground, text) end local function drawCross(x, y) @@ -332,16 +323,16 @@ local function drawCross(x, y) end OCGLView.draw = function(object) - application.oldClock = os.clock() + workspace.oldClock = os.clock() if world then renderWorld() end scene:render() - if application.toolbar.zBufferSwitch.state then + if workspace.toolbar.zBufferSwitch.state then renderer.visualizeDepthBuffer() end drawCross(renderer.viewport.xCenter, math.floor(renderer.viewport.yCenter / 2)) end -OCGLView.eventHandler = function(application, object, e1, e2, e3, e4, e5) +OCGLView.eventHandler = function(workspace, object, e1, e2, e3, e4, e5) if e1 == "touch" then local targetVector = vector.newVector3(scene.camera.position[1], scene.camera.position[2], scene.camera.position[3] + 1000) OCGL.rotateVectorRelativeToXAxis(targetVector, scene.camera.rotation[1]) @@ -368,122 +359,122 @@ OCGLView.eventHandler = function(application, object, e1, e2, e3, e4, e5) zWorld = zWorld - 1 end - setBlock(xWorld, yWorld, zWorld, e5 == 1 and application.toolbar.blockColorSelector.color or nil) + setBlock(xWorld, yWorld, zWorld, e5 == 1 and workspace.toolbar.blockColorSelector.color or nil) end end end -application:addChild(OCGLView) +workspace:addChild(OCGLView) -application.infoTextBox = application:addChild(GUI.textBox(2, 4, 45, application.height, nil, 0xEEEEEE, {}, 1, 0, 0)) +workspace.infoTextBox = workspace:addChild(GUI.textBox(2, 4, 45, workspace.height, nil, 0xEEEEEE, {}, 1, 0, 0)) local lines = { "Copyright © 2016-2017 - Developed by ECS Inc.", "Timofeef Igor (vk.com/id7799889), Trifonov Gleb (vk.com/id88323331), Verevkin Yakov (vk.com/id60991376), Bogushevich Victoria (vk.com/id171497518)", "All rights reserved", } -application:addChild(GUI.textBox(1, application.height - #lines + 1, application.width, #lines, nil, 0x3C3C3C, lines, 1)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) +workspace:addChild(GUI.textBox(1, workspace.height - #lines + 1, workspace.width, #lines, nil, 0x3C3C3C, lines, 1)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) local elementY = 2 -application.toolbar = application:addChild(GUI.container(application.width - 31, 1, 32, application.height)) -local elementWidth = application.toolbar.width - 2 -application.toolbar:addChild(GUI.panel(1, 1, application.toolbar.width, application.toolbar.height, 0x0, 0.5)) +workspace.toolbar = workspace:addChild(GUI.container(workspace.width - 31, 1, 32, workspace.height)) +local elementWidth = workspace.toolbar.width - 2 +workspace.toolbar:addChild(GUI.panel(1, 1, workspace.toolbar.width, workspace.toolbar.height, 0x0, 0.5)) -application.toolbar:addChild(GUI.label(2, elementY, elementWidth, 1, 0xEEEEEE, "Render mode")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); elementY = elementY + 2 -application.toolbar.renderModeComboBox = application.toolbar:addChild(GUI.comboBox(2, elementY, elementWidth, 1, 0x2D2D2D, 0xAAAAAA, 0x555555, 0x888888)); elementY = elementY + application.toolbar.renderModeComboBox.height + 1 -application.toolbar.renderModeComboBox:addItem("disabled") -application.toolbar.renderModeComboBox:addItem("constantShading") -application.toolbar.renderModeComboBox:addItem("flatShading") -application.toolbar.renderModeComboBox.selectedItem = scene.renderMode -application.toolbar.renderModeComboBox.onItemSelected = function() - scene.renderMode = application.toolbar.renderModeComboBox.selectedItem +workspace.toolbar:addChild(GUI.label(2, elementY, elementWidth, 1, 0xEEEEEE, "Render mode")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); elementY = elementY + 2 +workspace.toolbar.renderModeComboBox = workspace.toolbar:addChild(GUI.comboBox(2, elementY, elementWidth, 1, 0x2D2D2D, 0xAAAAAA, 0x555555, 0x888888)); elementY = elementY + workspace.toolbar.renderModeComboBox.height + 1 +workspace.toolbar.renderModeComboBox:addItem("disabled") +workspace.toolbar.renderModeComboBox:addItem("constantShading") +workspace.toolbar.renderModeComboBox:addItem("flatShading") +workspace.toolbar.renderModeComboBox.selectedItem = scene.renderMode +workspace.toolbar.renderModeComboBox.onItemSelected = function() + scene.renderMode = workspace.toolbar.renderModeComboBox.selectedItem end -application.toolbar.auxiliaryModeComboBox = application.toolbar:addChild(GUI.comboBox(2, elementY, elementWidth, 1, 0x2D2D2D, 0xAAAAAA, 0x555555, 0x888888)); elementY = elementY + application.toolbar.auxiliaryModeComboBox.height + 1 -application.toolbar.auxiliaryModeComboBox:addItem("disabled") -application.toolbar.auxiliaryModeComboBox:addItem("wireframe") -application.toolbar.auxiliaryModeComboBox:addItem("vertices") -application.toolbar.auxiliaryModeComboBox.selectedItem = scene.auxiliaryMode -application.toolbar.auxiliaryModeComboBox.onItemSelected = function() - scene.auxiliaryMode = application.toolbar.auxiliaryModeComboBox.selectedItem +workspace.toolbar.auxiliaryModeComboBox = workspace.toolbar:addChild(GUI.comboBox(2, elementY, elementWidth, 1, 0x2D2D2D, 0xAAAAAA, 0x555555, 0x888888)); elementY = elementY + workspace.toolbar.auxiliaryModeComboBox.height + 1 +workspace.toolbar.auxiliaryModeComboBox:addItem("disabled") +workspace.toolbar.auxiliaryModeComboBox:addItem("wireframe") +workspace.toolbar.auxiliaryModeComboBox:addItem("vertices") +workspace.toolbar.auxiliaryModeComboBox.selectedItem = scene.auxiliaryMode +workspace.toolbar.auxiliaryModeComboBox.onItemSelected = function() + scene.auxiliaryMode = workspace.toolbar.auxiliaryModeComboBox.selectedItem end -application.toolbar:addChild(GUI.label(2, elementY, elementWidth, 1, 0xAAAAAA, "Perspective proj:")) -application.toolbar.perspectiveSwitch = application.toolbar:addChild(GUI.switch(application.toolbar.width - 8, elementY, 8, 0x66DB80, 0x2D2D2D, 0xEEEEEE, scene.camera.projectionEnabled)); elementY = elementY + 2 -application.toolbar.perspectiveSwitch.onStateChanged = function() - scene.camera.projectionEnabled = application.toolbar.perspectiveSwitch.state +workspace.toolbar:addChild(GUI.label(2, elementY, elementWidth, 1, 0xAAAAAA, "Perspective proj:")) +workspace.toolbar.perspectiveSwitch = workspace.toolbar:addChild(GUI.switch(workspace.toolbar.width - 8, elementY, 8, 0x66DB80, 0x2D2D2D, 0xEEEEEE, scene.camera.projectionEnabled)); elementY = elementY + 2 +workspace.toolbar.perspectiveSwitch.onStateChanged = function() + scene.camera.projectionEnabled = workspace.toolbar.perspectiveSwitch.state end -application.toolbar:addChild(GUI.label(2, elementY, elementWidth, 1, 0xAAAAAA, "Z-buffer visualize:")) -application.toolbar.zBufferSwitch = application.toolbar:addChild(GUI.switch(application.toolbar.width - 8, elementY, 8, 0x66DB80, 0x2D2D2D, 0xEEEEEE, false)); elementY = elementY + 2 +workspace.toolbar:addChild(GUI.label(2, elementY, elementWidth, 1, 0xAAAAAA, "Z-buffer visualize:")) +workspace.toolbar.zBufferSwitch = workspace.toolbar:addChild(GUI.switch(workspace.toolbar.width - 8, elementY, 8, 0x66DB80, 0x2D2D2D, 0xEEEEEE, false)); elementY = elementY + 2 local function calculateLightComboBox() - application.toolbar.lightSelectComboBox.dropDownMenu.itemsContainer.children = {} + workspace.toolbar.lightSelectComboBox.dropDownMenu.itemsContainer.children = {} for i = 1, #scene.lights do - application.toolbar.lightSelectComboBox:addItem(tostring(i)) + workspace.toolbar.lightSelectComboBox:addItem(tostring(i)) end - application.toolbar.lightSelectComboBox.selectedItem = #application.toolbar.lightSelectComboBox.dropDownMenu.itemsContainer.children - application.toolbar.lightIntensitySlider.value = scene.lights[application.toolbar.lightSelectComboBox.selectedItem].intensity * 100 - application.toolbar.lightEmissionSlider.value = scene.lights[application.toolbar.lightSelectComboBox.selectedItem].emissionDistance + workspace.toolbar.lightSelectComboBox.selectedItem = #workspace.toolbar.lightSelectComboBox.dropDownMenu.itemsContainer.children + workspace.toolbar.lightIntensitySlider.value = scene.lights[workspace.toolbar.lightSelectComboBox.selectedItem].intensity * 100 + workspace.toolbar.lightEmissionSlider.value = scene.lights[workspace.toolbar.lightSelectComboBox.selectedItem].emissionDistance end -application.toolbar:addChild(GUI.label(2, elementY, elementWidth, 1, 0xEEEEEE, "Light control")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); elementY = elementY + 2 -application.toolbar.lightSelectComboBox = application.toolbar:addChild(GUI.comboBox(2, elementY, elementWidth, 1, 0x2D2D2D, 0xAAAAAA, 0x555555, 0x888888)); elementY = elementY + application.toolbar.lightSelectComboBox.height + 1 +workspace.toolbar:addChild(GUI.label(2, elementY, elementWidth, 1, 0xEEEEEE, "Light control")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); elementY = elementY + 2 +workspace.toolbar.lightSelectComboBox = workspace.toolbar:addChild(GUI.comboBox(2, elementY, elementWidth, 1, 0x2D2D2D, 0xAAAAAA, 0x555555, 0x888888)); elementY = elementY + workspace.toolbar.lightSelectComboBox.height + 1 -application.toolbar.addLightButton = application.toolbar:addChild(GUI.button(2, elementY, elementWidth, 1, 0x2D2D2D, 0xAAAAAA, 0x555555, 0xAAAAAA, "Add light")); elementY = elementY + 2 -application.toolbar.addLightButton.onTouch = function() - scene:addLight(meowEngine.newLight(vector.newVector3(0, 10, 0), application.toolbar.lightIntensitySlider.value / 100, application.toolbar.lightEmissionSlider.value)) +workspace.toolbar.addLightButton = workspace.toolbar:addChild(GUI.button(2, elementY, elementWidth, 1, 0x2D2D2D, 0xAAAAAA, 0x555555, 0xAAAAAA, "Add light")); elementY = elementY + 2 +workspace.toolbar.addLightButton.onTouch = function() + scene:addLight(meowEngine.newLight(vector.newVector3(0, 10, 0), workspace.toolbar.lightIntensitySlider.value / 100, workspace.toolbar.lightEmissionSlider.value)) calculateLightComboBox() end -application.toolbar.removeLightButton = application.toolbar:addChild(GUI.button(2, elementY, elementWidth, 1, 0x2D2D2D, 0xAAAAAA, 0x555555, 0xAAAAAA, "Remove light")); elementY = elementY + 2 -application.toolbar.removeLightButton.onTouch = function() +workspace.toolbar.removeLightButton = workspace.toolbar:addChild(GUI.button(2, elementY, elementWidth, 1, 0x2D2D2D, 0xAAAAAA, 0x555555, 0xAAAAAA, "Remove light")); elementY = elementY + 2 +workspace.toolbar.removeLightButton.onTouch = function() if #scene.lights > 1 then - table.remove(scene.lights, application.toolbar.lightSelectComboBox.selectedItem) + table.remove(scene.lights, workspace.toolbar.lightSelectComboBox.selectedItem) calculateLightComboBox() end end -application.toolbar.lightIntensitySlider = application.toolbar:addChild(GUI.slider(2, elementY, elementWidth, 0xCCCCCC, 0x2D2D2D, 0xEEEEEE, 0xAAAAAA, 0, 500, 100, false, "Intensity: ", "")); elementY = elementY + 3 -application.toolbar.lightIntensitySlider.onValueChanged = function() - scene.lights[application.toolbar.lightSelectComboBox.selectedItem].intensity = application.toolbar.lightIntensitySlider.value / 100 +workspace.toolbar.lightIntensitySlider = workspace.toolbar:addChild(GUI.slider(2, elementY, elementWidth, 0xCCCCCC, 0x2D2D2D, 0xEEEEEE, 0xAAAAAA, 0, 500, 100, false, "Intensity: ", "")); elementY = elementY + 3 +workspace.toolbar.lightIntensitySlider.onValueChanged = function() + scene.lights[workspace.toolbar.lightSelectComboBox.selectedItem].intensity = workspace.toolbar.lightIntensitySlider.value / 100 end -application.toolbar.lightEmissionSlider = application.toolbar:addChild(GUI.slider(2, elementY, elementWidth, 0xCCCCCC, 0x2D2D2D, 0xEEEEEE, 0xAAAAAA, 0, scene.lights[application.toolbar.lightSelectComboBox.selectedItem].emissionDistance, scene.lights[application.toolbar.lightSelectComboBox.selectedItem].emissionDistance, false, "Distance: ", "")); elementY = elementY + 3 -application.toolbar.lightEmissionSlider.onValueChanged = function() - scene.lights[application.toolbar.lightSelectComboBox.selectedItem].emissionDistance = application.toolbar.lightEmissionSlider.value +workspace.toolbar.lightEmissionSlider = workspace.toolbar:addChild(GUI.slider(2, elementY, elementWidth, 0xCCCCCC, 0x2D2D2D, 0xEEEEEE, 0xAAAAAA, 0, scene.lights[workspace.toolbar.lightSelectComboBox.selectedItem].emissionDistance, scene.lights[workspace.toolbar.lightSelectComboBox.selectedItem].emissionDistance, false, "Distance: ", "")); elementY = elementY + 3 +workspace.toolbar.lightEmissionSlider.onValueChanged = function() + scene.lights[workspace.toolbar.lightSelectComboBox.selectedItem].emissionDistance = workspace.toolbar.lightEmissionSlider.value end calculateLightComboBox() -application.toolbar.blockColorSelector = application.toolbar:addChild(GUI.colorSelector(2, elementY, elementWidth, 1, 0xEEEEEE, "Block color")); elementY = elementY + application.toolbar.blockColorSelector.height + 1 -application.toolbar.backgroundColorSelector = application.toolbar:addChild(GUI.colorSelector(2, elementY, elementWidth, 1, scene.backgroundColor, "Background color")); elementY = elementY + application.toolbar.blockColorSelector.height + 1 -application.toolbar.backgroundColorSelector.onColorSelected = function() - scene.backgroundColor = application.toolbar.backgroundColorSelector.color +workspace.toolbar.blockColorSelector = workspace.toolbar:addChild(GUI.colorSelector(2, elementY, elementWidth, 1, 0xEEEEEE, "Block color")); elementY = elementY + workspace.toolbar.blockColorSelector.height + 1 +workspace.toolbar.backgroundColorSelector = workspace.toolbar:addChild(GUI.colorSelector(2, elementY, elementWidth, 1, scene.backgroundColor, "Background color")); elementY = elementY + workspace.toolbar.blockColorSelector.height + 1 +workspace.toolbar.backgroundColorSelector.onColorSelected = function() + scene.backgroundColor = workspace.toolbar.backgroundColorSelector.color end -application.toolbar:addChild(GUI.label(2, elementY, elementWidth, 1, 0xEEEEEE, "RAM monitoring")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); elementY = elementY + 2 -application.toolbar.RAMChart = application.toolbar:addChild(GUI.chart(2, elementY, elementWidth, application.toolbar.height - elementY - 3, 0xEEEEEE, 0xAAAAAA, 0x555555, 0x66DB80, 0.35, 0.25, "s", "%", true, {})); elementY = elementY + application.toolbar.RAMChart.height + 1 -application.toolbar.RAMChart.roundValues = true --- application.toolbar.RAMChart.showXAxisValues = false -application.toolbar.RAMChart.counter = 1 +workspace.toolbar:addChild(GUI.label(2, elementY, elementWidth, 1, 0xEEEEEE, "RAM monitoring")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); elementY = elementY + 2 +workspace.toolbar.RAMChart = workspace.toolbar:addChild(GUI.chart(2, elementY, elementWidth, workspace.toolbar.height - elementY - 3, 0xEEEEEE, 0xAAAAAA, 0x555555, 0x66DB80, 0.35, 0.25, "s", "%", true, {})); elementY = elementY + workspace.toolbar.RAMChart.height + 1 +workspace.toolbar.RAMChart.roundValues = true +-- workspace.toolbar.RAMChart.showXAxisValues = false +workspace.toolbar.RAMChart.counter = 1 -application.toolbar:addChild(GUI.button(1, application.toolbar.height - 2, application.toolbar.width, 3, 0x2D2D2D, 0xEEEEEE, 0x444444, 0xEEEEEE, "Exit")).onTouch = function() - application:stop() +workspace.toolbar:addChild(GUI.button(1, workspace.toolbar.height - 2, workspace.toolbar.width, 3, 0x2D2D2D, 0xEEEEEE, 0x444444, 0xEEEEEE, "Exit")).onTouch = function() + workspace:stop() end local FPSCounter = GUI.object(2, 2, 8, 3) FPSCounter.draw = function(FPSCounter) - renderer.renderFPSCounter(FPSCounter.x, FPSCounter.y, tostring(math.ceil(1 / (os.clock() - application.oldClock) / 10)), 0xFFFF00) + renderer.renderFPSCounter(FPSCounter.x, FPSCounter.y, tostring(math.ceil(1 / (os.clock() - workspace.oldClock) / 10)), 0xFFFF00) end -application:addChild(FPSCounter) +workspace:addChild(FPSCounter) -application.eventHandler = function(application, object, e1, e2, e3, e4, e5) - if not application.toolbar.hidden then +workspace.eventHandler = function(workspace, object, e1, e2, e3, e4, e5) + if not workspace.toolbar.hidden then local totalMemory = computer.totalMemory() - table.insert(application.toolbar.RAMChart.values, {application.toolbar.RAMChart.counter, math.ceil((totalMemory - computer.freeMemory()) / totalMemory * 100)}) - application.toolbar.RAMChart.counter = application.toolbar.RAMChart.counter + 1 - if #application.toolbar.RAMChart.values > 20 then table.remove(application.toolbar.RAMChart.values, 1) end + table.insert(workspace.toolbar.RAMChart.values, {workspace.toolbar.RAMChart.counter, math.ceil((totalMemory - computer.freeMemory()) / totalMemory * 100)}) + workspace.toolbar.RAMChart.counter = workspace.toolbar.RAMChart.counter + 1 + if #workspace.toolbar.RAMChart.values > 20 then table.remove(workspace.toolbar.RAMChart.values, 1) end - application.infoTextBox.lines = { + workspace.infoTextBox.lines = { " ", "SceneObjects: " .. #scene.objects, " ", @@ -510,7 +501,7 @@ application.eventHandler = function(application, object, e1, e2, e3, e4, e5) "F1 - toggle GUI overlay", } - application.infoTextBox.height = #application.infoTextBox.lines + workspace.infoTextBox.height = #workspace.infoTextBox.lines end if e1 == "key_down" then @@ -529,9 +520,9 @@ application.eventHandler = function(application, object, e1, e2, e3, e4, e5) end end - application:draw() + workspace:draw() end -------------------------------------------------------- Ebat-kopat -------------------------------------------------------- -application:start(0) \ No newline at end of file +workspace:start(0) \ No newline at end of file diff --git a/Applications/3DTest/About/English.txt b/Applications/3DTest/About/English.txt deleted file mode 100644 index fb6b4036..00000000 --- a/Applications/3DTest/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -This is a program for demostration of the capabilities of our 3D-engine, created specifically for low-performance computers. It embodies almost all the achievements of our team: rendering complex 3D-objects, dynamic lighting, texturing and user interface based on a powerful GUI library. \ No newline at end of file diff --git a/Applications/3DTest/About/Russian.txt b/Applications/3DTest/About/Russian.txt deleted file mode 100644 index 86e147be..00000000 --- a/Applications/3DTest/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Программа-демонстратор возможностей 3D-движка, созданного специально для низкопроизводительных компьютеров. В ней воплощены практически все наработки нашей команды: от отрисовки сложных трехмерных объектов и динамического освещения до текстурирования и пользовательского интерфейса на мощной GUI-библиотеке. \ No newline at end of file diff --git a/Applications/App Market.app/Icon.pic b/Applications/App Market.app/Icon.pic new file mode 100644 index 0000000000000000000000000000000000000000..8c4d7ff56c5a03a6b4e7a8f8e9c8350d6d052dff GIT binary patch literal 117 zcmXYo!3o1a3= version then return 4 else @@ -302,7 +311,7 @@ end local function ratingWidgetDraw(object) local x = 0 for i = 1, 5 do - buffer.drawText(object.x + x, object.y, object.rating >= i and object.colors.first or object.colors.second, "*") + screen.drawText(object.x + x, object.y, object.rating >= i and object.colors.first or object.colors.second, "*") x = x + object.spacing end @@ -324,7 +333,7 @@ local function newRatingWidget(x, y, rating, firstColor, secondColor) end local function deletePublication(publication) - local container = MineOSInterface.addBackgroundContainer(MineOSInterface.application, localization.areYouSure) + local container = GUI.addBackgroundContainer(workspace, true, true, localization.areYouSure) local buttonsLayout = container.layout:addChild(newButtonsLayout(1, 1, container.layout.width, 3)) buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 2, 0, 0xE1E1E1, 0x2D2D2D, 0x0, 0xE1E1E1, localization.yes)).onTouch = function() @@ -343,7 +352,7 @@ local function deletePublication(publication) buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 2, 0, 0xA5A5A5, 0x2D2D2D, 0x0, 0xE1E1E1, localization.no)).onTouch = function() container:remove() - MineOSInterface.application:draw() + workspace:draw() end end @@ -373,8 +382,6 @@ local function getDependencyPath(mainFilePath, dependency) end local function download(publication) - activity(true) - if not publication.translated_description then publication = fieldAPIRequest("result", "publication", { file_id = publication.file_id, @@ -383,7 +390,7 @@ local function download(publication) end if publication then - local container = MineOSInterface.addBackgroundContainer(MineOSInterface.application, localization.choosePath) + local container = GUI.addBackgroundContainer(workspace, true, true, localization.choosePath) local filesystemChooserPath = fileVersions[publication.file_id] and getApplicationPathFromVersions(fileVersions[publication.file_id].path) if not filesystemChooserPath then @@ -426,7 +433,7 @@ local function download(publication) local idiNahooy = dependencyTree local dependencyPath = getDependencyPath(mainFilePath, treeData[i]) - for blyad in fs.path(dependencyPath):gmatch("[^/]+") do + for blyad in filesystem.path(dependencyPath):gmatch("[^/]+") do if not idiNahooy[blyad] then idiNahooy[blyad] = {} end @@ -443,7 +450,7 @@ local function download(publication) local function pizda(t, offset, initPath) for chlen, devka in pairs(t) do if devka.path then - tree:addItem(fs.name(devka.path), devka.path, offset, false) + tree:addItem(filesystem.name(devka.path), devka.path, offset, false) else tree:addItem(chlen, chlen, offset, true) tree.expandedItems[chlen] = true @@ -466,9 +473,9 @@ local function download(publication) local countOfShit = 1 + (publication.all_dependencies and #publication.all_dependencies or 0) local function govnoed(pizda, i) - container.label.text = localization.downloading .. " " .. fs.name(pizda.path) - progressBar.value = math.round(i / countOfShit * 100) - MineOSInterface.application:draw() + container.label.text = localization.downloading .. " " .. filesystem.name(pizda.path) + progressBar.value = math.floor(i / countOfShit * 100) + workspace:draw() end -- SAVED @@ -494,7 +501,7 @@ local function download(publication) } tryToDownload(dependency.source_url, dependencyPath) else - os.sleep(0.05) + event.sleep(0.05) end end end @@ -503,18 +510,16 @@ local function download(publication) callLastMethod() if not shortcutSwitchAndLabel.hidden and shortcutSwitchAndLabel.switch.state then - MineOSCore.createShortcut(MineOSPaths.desktop .. fs.hideExtension(fs.name(filesystemChooser.path)) .. ".lnk", filesystemChooser.path .. "/") + system.createShortcut(paths.user.desktop .. filesystem.hideExtension(filesystem.name(filesystemChooser.path)) .. ".lnk", filesystemChooser.path .. "/") end - computer.pushSignal("MineOSCore", "updateFileList") + computer.pushSignal("system", "updateFileList") saveFileVersions() end filesystemChooser.onSubmit = updateTree updateTree() end - - activity() end local function addPanel(container, color) @@ -534,14 +539,12 @@ local function getPublicationIcon(publication) if publication.icon_url then local path = iconCachePath .. publication.file_id .. "@" .. publication.version .. ".pic" - if fs.exists(path) then + if filesystem.exists(path) then return loadImage(path) else local data, reason = checkImage(publication.icon_url) if data then - local file = io.open(path, "w") - file:write(data) - file:close() + filesystem.write(path, data) return loadImage(path) else @@ -558,9 +561,9 @@ end local function addApplicationInfo(container, publication, limit) addPanel(container) container.image = container:addChild(GUI.image(3, 2, getPublicationIcon(publication))) - container.nameLabel = container:addChild(GUI.text(13, 2, 0x0, string.limit(publication.publication_name, limit, "right"))) - container.developerLabel = container:addChild(GUI.text(13, 3, 0x878787, string.limit("©" .. publication.user_name, limit, "right"))) - container.rating = container:addChild(newRatingWidget(13, 4, publication.average_rating and math.round(publication.average_rating) or 0)) + container.nameLabel = container:addChild(GUI.text(13, 2, 0x0, text.limit(publication.publication_name, limit, "right"))) + container.developerLabel = container:addChild(GUI.text(13, 3, 0x878787, text.limit("©" .. publication.user_name, limit, "right"))) + container.rating = container:addChild(newRatingWidget(13, 4, publication.average_rating and number.round(publication.average_rating) or 0)) local updateState = getUpdateState(publication.file_id, publication.version) container.downloadButton = container:addChild(GUI.adaptiveRoundedButton(13, 5, 1, 0, 0xC3C3C3, 0xFFFFFF, 0x969696, 0xFFFFFF, updateState == 4 and localization.installed or updateState == 3 and localization.update or localization.install)) @@ -574,18 +577,18 @@ local function addApplicationInfo(container, publication, limit) if updateState > 2 then container.downloadButton.width = container.downloadButton.width + 1 container:addChild(GUI.adaptiveRoundedButton(container.downloadButton.localX + container.downloadButton.width, container.downloadButton.localY, 1, 0, 0xF0F0F0, 0x969696, 0x969696, 0xFFFFFF, "x")).onTouch = function() - fs.remove(getApplicationPathFromVersions(fileVersions[publication.file_id].path)) - fs.remove(MineOSPaths.desktop .. publication.publication_name .. ".lnk") + filesystem.remove(getApplicationPathFromVersions(fileVersions[publication.file_id].path)) + filesystem.remove(paths.user.desktop .. publication.publication_name .. ".lnk") fileVersions[publication.file_id] = nil callLastMethod() - computer.pushSignal("MineOSCore", "updateFileList") + computer.pushSignal("system", "updateFileList") saveFileVersions() end end end -local function containerScrollEventHandler(application, object, e1, e2, e3, e4, e5) +local function containerScrollEventHandler(workspace, object, e1, e2, e3, e4, e5) if e1 == "scroll" then local first, last = object.children[1], object.children[#object.children] @@ -594,25 +597,25 @@ local function containerScrollEventHandler(application, object, e1, e2, e3, e4, for i = 1, #object.children do object.children[i].localY = object.children[i].localY + 1 end - MineOSInterface.application:draw() + workspace:draw() end else if last.localY + last.height - 1 >= object.height then for i = 1, #object.children do object.children[i].localY = object.children[i].localY - 1 end - MineOSInterface.application:draw() + workspace:draw() end end end end -local newApplicationPreview, newPublicationInfo, mainMenu +local newApplicationPreview, newPublicationInfo -local function applicationWidgetEventHandler(application, object, e1) +local function applicationWidgetEventHandler(workspace, object, e1) if e1 == "touch" then object.parent.panel.colors.background = 0xE1E1E1 - MineOSInterface.application:draw() + workspace:draw() newPublicationInfo(object.parent.file_id) end end @@ -637,552 +640,481 @@ newApplicationPreview = function(x, y, publication) return container end -mainMenu = function(menuID, messageToUser) - window.tabBar.selectedItem = 1 - lastMethod, lastArguments = mainMenu, {menuID, messageToUser} +local function overview() + leftList.selectedItem = 1 + lastMethod, lastArguments = overview, {} contentContainer:removeChildren() - local menuList = contentContainer:addChild(GUI.list(1, 1, 23, contentContainer.height, 3, 0, 0xE1E1E1, 0x3C3C3C, 0xD2D2D2, 0x3C3C3C, 0x3C3C3C, 0xE1E1E1)) - local menuContentContainer = contentContainer:addChild(GUI.container(menuList.width + 1, 1, contentContainer.width - menuList.width, contentContainer.height)) - - local function statistics() - activity(true) - - local statistics = fieldAPIRequest("result", "statistics") - if statistics then - MineOSInterface.application:draw() - - local publications = fieldAPIRequest("result", "publications", { - order_by = "popularity", - order_direction = "desc", - offset = 0, - count = overviewIconsCount + 1, - category_id = 1, - }) - - if publications then - menuContentContainer:removeChildren() - - local iconsContainer = menuContentContainer:addChild(GUI.container(1, 1, menuContentContainer.width, menuContentContainer.height)) - - local width = 38 - local container = menuContentContainer:addChild(GUI.container(math.floor(menuContentContainer.width / 2 - width / 2), 1, width, menuContentContainer.height)) - container:addChild(GUI.panel(1, 1, container.width, container.height, 0xFFFFFF)) - - local statisticsLayout = container:addChild(GUI.layout(1, 1, container.width, container.height, 1, 1)) - - statisticsLayout:addChild(GUI.image(1, 1, image.load(currentScriptDirectory .. "Icon.pic"))).height = 5 - - local textLayout = statisticsLayout:addChild(GUI.layout(1, 1, container.width - 4, 1, 1, 1)) - textLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP) - - textLayout:addChild(GUI.keyAndValue(1, 1, 0x4B4B4B, 0xA5A5A5, localization.statisticsUsersCount, ": " .. statistics.users_count)) - textLayout:addChild(GUI.keyAndValue(1, 1, 0x4B4B4B, 0xA5A5A5, localization.statisticsNewUser, ": " .. statistics.last_registered_user)) - textLayout:addChild(GUI.keyAndValue(1, 1, 0x4B4B4B, 0xA5A5A5, localization.statisticsMostPopularUser, ": " .. statistics.most_popular_user)) - textLayout:addChild(GUI.keyAndValue(1, 1, 0x4B4B4B, 0xA5A5A5, localization.statisticsPublicationsCount, ": " .. statistics.publications_count)) - textLayout:addChild(GUI.keyAndValue(1, 1, 0x4B4B4B, 0xA5A5A5, localization.statisticsReviewsCount, ": " .. statistics.reviews_count)) - textLayout.height = #textLayout.children * 2 - 1 - - local applicationPreview = statisticsLayout:addChild(newApplicationPreview(1, 1, publications[1])) - applicationPreview.panel.colors.background = 0xF0F0F0 - statisticsLayout:addChild(GUI.label(1, 1, statisticsLayout.width, 1, 0xA5A5A5, localization.statisticsPopularPublication)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_CENTER) - - MineOSInterface.application:draw() - - local uptime, newUptime = computer.uptime() - local function tick() - newUptime = computer.uptime() - if newUptime - uptime > overviewAnimationDelay then - uptime = newUptime - - local child - for i = 1, #iconsContainer.children do - child = iconsContainer.children[i] - - child.moveX, child.moveY = child.moveX + child.forceX, child.moveY + child.forceY - - if child.forceX > 0 then - if child.forceX > overviewForceLimit then - child.forceX = child.forceX - overviewForceDecay - else - child.forceX = overviewForceLimit - end - else - if child.forceX < -overviewForceLimit then - child.forceX = child.forceX + overviewForceDecay - else - child.forceX = -overviewForceLimit - end - end - - if child.forceY > 0 then - if child.forceY > overviewForceLimit then - child.forceY = child.forceY - overviewForceDecay - else - child.forceY = overviewForceLimit - end - else - if child.forceY < -overviewForceLimit then - child.forceY = child.forceY + overviewForceDecay - else - child.forceY = -overviewForceLimit - end - end - - if child.moveX <= 1 then - child.forceX, child.moveX = -child.forceX, 1 - elseif child.moveX + child.width - 1 >= iconsContainer.width then - child.forceX, child.moveX = -child.forceX, iconsContainer.width - child.width + 1 - end - - if child.moveY <= 1 then - child.forceY, child.moveY = -child.forceY, 1 - elseif child.moveY + child.height - 1 >= iconsContainer.height then - child.forceY, child.moveY = -child.forceY, iconsContainer.height - child.height + 1 - end - - child.localX, child.localY = math.floor(child.moveX), math.floor(child.moveY) - end - - MineOSInterface.application:draw() - - return true - end - end - - iconsContainer.eventHandler = function(application, object, e1, e2, e3, e4) - if e1 == "touch" or e1 == "drag" then - local child, deltaX, deltaY, vectorLength - for i = 1, #iconsContainer.children do - child = iconsContainer.children[i] - - deltaX, deltaY = e3 - child.x, e4 - child.y - vectorLength = math.sqrt(deltaX ^ 2 + deltaY ^ 2) - if vectorLength > 0 then - child.forceX = deltaX / vectorLength * math.random(overviewMaximumTouchAcceleration) - child.forceY = deltaY / vectorLength * math.random(overviewMaximumTouchAcceleration) - end - end - end - - tick() - end - - local function makeBlyad(object) - object.localX = math.random(1, iconsContainer.width - object.width + 1) - object.localY = math.random(1, iconsContainer.height - object.width + 1) - object.moveX = object.localX - object.moveY = object.localY - object.forceX = math.random(-100, 100) / 100 * overviewForceLimit - object.forceY = math.random(-100, 100) / 100 * overviewForceLimit - - if not tick() then - MineOSInterface.application:draw() - end - end - - for i = 2, #publications do - makeBlyad(iconsContainer:addChild(GUI.image(1, 1, getPublicationIcon(publications[i])), 1)) - end - end - end - - activity() - end - - local function dialogGUI(to_user_name) - local messages - if to_user_name then - activity(true) - - local result = fieldAPIRequest("result", "messages", { - token = user.token, - user_name = to_user_name - }) - - if result then - messages = result - end - - activity() - end - messages = messages or {} - - menuContentContainer:removeChildren() - - local button = menuContentContainer:addChild(GUI.adaptiveButton(1, menuContentContainer.height - 2, 2, 1, 0x4B4B4B, 0xE1E1E1, 0x2D2D2D, 0xE1E1E1, localization.send)) - button.localX = menuContentContainer.width - button.width + 1 - button.colors.disabled.background = 0xB4B4B4 - button.colors.disabled.text = 0xFFFFFF - button.disabled = true - - local input = menuContentContainer:addChild(GUI.input(1, button.localY, menuContentContainer.width - button.width, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", localization.typeMessageText)) - - local function check() - button.disabled = not (to_user_name and #input.text > 0) - end - - input.onInputFinished = check - - button.onTouch = function() - activity(true) - - local success, reason = RawAPIRequest("message", { - token = user.token, - user_name = to_user_name, - text = input.text - }) - - if success then - dialogGUI(to_user_name) - else - GUI.alert(reason) - end - - activity(false) - end - - local messagesContainer = menuContentContainer:addChild(GUI.container(1, 4, menuContentContainer.width, menuContentContainer.height - 6)) - messagesContainer.eventHandler = containerScrollEventHandler - - local panel = menuContentContainer:addChild(GUI.panel(1, 1, menuContentContainer.width, 3, 0xFFFFFF)) - if not to_user_name then - panel.colors.transparency = nil - local text = menuContentContainer:addChild(GUI.text(3, 2, 0x0, localization.toWho)) - local input = menuContentContainer:addChild(GUI.input(text.localX + text.width, 1, menuContentContainer.width - text.width - 4, 3, 0xFFFFFF, 0x878787, 0xC3C3C3, 0xFFFFFF, 0x878787, to_user_name or "", localization.typeUserName)) - input.onInputFinished = function() - to_user_name = input.text - check() - end - else - local keyAndValue = menuContentContainer:addChild(GUI.keyAndValue(1, 2, 0x878787, 0x0, localization.dialogWith, to_user_name)) - keyAndValue.localX = math.floor(menuContentContainer.width / 2 - (keyAndValue.keyLength + keyAndValue.valueLength) / 2) - -- menuContentContainer:addChild(GUI.label(1, 2, menuContentContainer.width, 1, 0x2D2D2D, to_user_name)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_CENTER) - end - - local function cloudDraw(object) - local backgroundColor, textColor = object.me and 0x6692FF or 0xFFFFFF, object.me and 0xFFFFFF or 0x4B4B4B - - buffer.drawRectangle(object.x, object.y, object.width, object.height, backgroundColor, textColor, " ") - buffer.drawText(object.x, object.y - 1, backgroundColor, "⢀" .. string.rep("⣀", object.width - 2) .. "⡀") - buffer.drawText(object.x, object.y + object.height, backgroundColor, "⠈" .. string.rep("⠉", object.width - 2) .. "⠁") - - local date = os.date("%d.%m.%Y, %H:%M", object.timestamp) - if object.me then - buffer.drawText(object.x - #date - 1, object.y, 0xC3C3C3, date) - buffer.drawText(object.x + object.width, object.y + object.height - 1, backgroundColor, "◤") - else - buffer.drawText(object.x + object.width + 1, object.y, 0xC3C3C3, date) - buffer.drawText(object.x - 1, object.y + object.height - 1, backgroundColor, "◥") - end - - for i = 1, #object.lines do - buffer.drawText(object.x + 1, object.y + i - 1, textColor, object.lines[i]) - end - end - - local function newCloud(y, width, text, me, timestamp) - local lines = string.wrap(text, width - 2) - local object = GUI.object(me and messagesContainer.width - width - 1 or 3, y - #lines + 1, width, 1) - - object.lines = lines - object.height = #lines - object.me = me - object.text = text - object.draw = cloudDraw - object.timestamp = timestamp - - return object - end - - local y = messagesContainer.height - 1 - for j = 1, #messages do - local cloud = messagesContainer:addChild(newCloud(y, math.floor(messagesContainer.width * 0.6), tostring(messages[j].text), messages[j].user_name == user.name, messages[j].timestamp), 1) - y = y - cloud.height - 2 - end - - -- Пустой объект для прокрутки ниже этой прозрачной пизды - messagesContainer:addChild(GUI.object(1, y, 1, 2), 1) - end - - local function dialogs() - activity(true) - - local dialogs = fieldAPIRequest("result", "dialogs", { - token = user.token, + local statistics = fieldAPIRequest("result", "statistics") + if statistics then + local publications = fieldAPIRequest("result", "publications", { + order_by = "popularity", + order_direction = "desc", + offset = 0, + count = overviewIconsCount + 1, + category_id = 1, }) - if dialogs then - menuContentContainer:removeChildren() + if publications then + contentContainer:removeChildren() - local dialogsContainer = menuContentContainer:addChild(GUI.container(1, 1, menuContentContainer.width, menuContentContainer.height)) - dialogsContainer.eventHandler = containerScrollEventHandler + local iconsContainer = contentContainer:addChild(GUI.container(1, 1, contentContainer.width, contentContainer.height)) - local sendMessageButton = dialogsContainer:addChild(GUI.adaptiveRoundedButton(1, #dialogs > 0 and 2 or math.floor(dialogsContainer.height / 2 + 1), 2, 0, 0x696969, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.newMessage)) - sendMessageButton.localX = math.floor(dialogsContainer.width / 2 - sendMessageButton.width / 2) - sendMessageButton.onTouch = function() - dialogGUI(nil, {}) - end - - if #dialogs > 0 then - local y = sendMessageButton.localY + 2 - - for i = 1, #dialogs do - local backgroundColor, nicknameColor, timestampColor, textColor = 0xFFFFFF, 0x0, 0xD2D2D2, 0x969696 - if dialogs[i].last_message_is_read == 0 and dialogs[i].last_message_user_name ~= user.name then - backgroundColor, nicknameColor, timestampColor, textColor = 0xCCDBFF, 0x0, 0xB4B4B4, 0x878787 - end - - local dialogContainer = dialogsContainer:addChild(GUI.container(3, y, dialogsContainer.width - 4, 4)) - addPanel(dialogContainer,backgroundColor) - - dialogContainer:addChild(GUI.keyAndValue(3, 2, nicknameColor, timestampColor, dialogs[i].dialog_user_name, os.date(" (%d.%m.%Y, %H:%M)", dialogs[i].timestamp))) - dialogContainer:addChild(GUI.text(3, 3, textColor, string.limit((dialogs[i].last_message_user_name == user.name and localization.yourText .. " " or "") .. dialogs[i].text, dialogContainer.width - 4, "right"))) - - dialogContainer.eventHandler = function(application, object, e1) - if e1 == "touch" then - dialogContainer.panel.colors.background = 0xE1E1E1 - dialogGUI(dialogs[i].dialog_user_name) - end - end - - y = y + dialogContainer.height + 1 - end - else - dialogsContainer:addChild(GUI.label(1, sendMessageButton.localY - 2, dialogsContainer.width, 1, 0xA5A5A5, localization.hereBeYourDialogs)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_CENTER) - end - end - - activity() - end - - local function settings() - menuContentContainer:removeChildren() - local layout = menuContentContainer:addChild(GUI.layout(1, 1, menuContentContainer.width, menuContentContainer.height, 1, 1)) - - layout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.language)) - - local languageComboBox = layout:addChild(GUI.comboBox(1, 1, 36, 1, 0xFFFFFF, 0x696969, 0x969696, 0xE1E1E1)) - for key, value in pairs(languages) do - languageComboBox:addItem(value).onTouch = function() - config.language_id = key - saveConfig() - end - - if key == config.language_id then - languageComboBox.selectedItem = languageComboBox:count() - end - end - - layout:addChild(GUI.adaptiveRoundedButton(1, 1, 2, 0, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.clearCache)).onTouch = function() - for file in fs.list(iconCachePath) do - fs.remove(iconCachePath .. file) - end - end - - MineOSInterface.application:draw() - end - - local function account() - local function logout() - user = {} - saveUser() - mainMenu(2) - end - - local function addAccountShit(login, register, recover) - menuContentContainer:removeChildren() - local layout = menuContentContainer:addChild(GUI.layout(1, 1, menuContentContainer.width, menuContentContainer.height, 1, 1)) + local width = appWidth + 4 + local container = contentContainer:addChild(GUI.container(math.floor(contentContainer.width / 2 - width / 2), 1, width, contentContainer.height)) + container:addChild(GUI.panel(1, 1, container.width, container.height, 0xFFFFFF)) - layout:addChild(GUI.label(1, 1, 36, 1, 0x0, login and localization.login or register and localization.createAccount or recover and localization.changePassword)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) - local nameInput = layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", localization.nickname)) - local emailInput = layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", login and localization.nicknameOrEmail or "E-mail")) - local currentPasswordInput = layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", localization.currentPassword, false, "*")) - local passwordInput = layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", recover and localization.newPassword or localization.password, false, "*")) + local statisticsLayout = container:addChild(GUI.layout(1, 1, container.width, container.height, 1, 1)) - local singleSessionSwitchAndLabel = layout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xC3C3C3, 0xFFFFFF, 0xA5A5A5, localization.singleSession .. ":", config.singleSession)) + statisticsLayout:addChild(GUI.image(1, 1, image.load(currentScriptDirectory .. "Icon.pic"))).height = 5 + + local textLayout = statisticsLayout:addChild(GUI.layout(1, 1, container.width - 4, 1, 1, 1)) + textLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP) - layout:addChild(GUI.button(1, 1, 36, 3, 0xA5A5A5, 0xFFFFFF, 0x696969, 0xFFFFFF, "OK")).onTouch = function() - activity(true) - - if login then - local result = fieldAPIRequest("result", "login", { - [(string.find(emailInput.text, "@") and "email" or "name")] = emailInput.text, - password = passwordInput.text - }) - - if result then - user = { - token = result.token, - name = result.name, - id = result.id, - email = result.email, - timestamp = result.timestamp, - } - - mainMenu(3) - - config.singleSession = singleSessionSwitchAndLabel.switch.state - if not config.singleSession then - saveUser() - end - saveConfig() - end - elseif register then - local result = fieldAPIRequest("result", "register", { - name = nameInput.text, - email = emailInput.text, - password = passwordInput.text, - }) - - if result then - showLabelAsContent(menuContentContainer, localization.registrationSuccessfull) - end - else - local success, reason = RawAPIRequest("change_password", { - email = user.email, - current_password = currentPasswordInput.text, - new_password = passwordInput.text, - }) - - if success then - logout() - else - GUI.alert(reason) - end - end - - activity() + local function add(key, value) + textLayout:addChild(GUI.keyAndValue(1, 1, 0x4B4B4B, 0xA5A5A5, key, ": " .. value)) end - if login then - local registerLayout = layout:addChild(GUI.layout(1, 1, layout.width, 1, 1, 1)) - registerLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL) - registerLayout:addChild(GUI.text(1, 1, 0xA5A5A5, localization.notRegistered)) - registerLayout:addChild(GUI.adaptiveButton(1, 1, 0, 0, nil, 0x696969, nil, 0x0, localization.createAccount)).onTouch = function() - addAccountShit(false, true, false) - end - end + add(localization.statisticsUsersCount, statistics.users_count) + add(localization.statisticsNewUser, statistics.last_registered_user) + add(localization.statisticsMostPopularUser, statistics.most_popular_user) + add(localization.statisticsPublicationsCount, statistics.publications_count) + add(localization.statisticsReviewsCount, statistics.reviews_count) + add(localization.statisticsMessagesCount, statistics.messages_count) + + textLayout.height = #textLayout.children * 2 - currentPasswordInput.hidden = not recover - emailInput.hidden = recover - nameInput.hidden = login or recover - singleSessionSwitchAndLabel.hidden = not login - end + local applicationPreview = statisticsLayout:addChild(newApplicationPreview(1, 1, publications[1])) + applicationPreview.panel.colors.background = 0xF0F0F0 + statisticsLayout:addChild(GUI.label(1, 1, statisticsLayout.width, 1, 0xA5A5A5, localization.statisticsPopularPublication)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_CENTER) - if user.token then - activity(true) + workspace:draw() - local result = fieldAPIRequest("result", "publications", { - user_name = user.name, - order_by = "name", - order_direction = "asc", - }) + local uptime, newUptime = computer.uptime() + local function tick() + newUptime = computer.uptime() + if newUptime - uptime > overviewAnimationDelay then + uptime = newUptime - if result then - menuContentContainer:removeChildren() - local layout = menuContentContainer:addChild(GUI.layout(1, 1, menuContentContainer.width, menuContentContainer.height, 1, 1)) - - layout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.profile)) - - local textLayout = layout:addChild(GUI.layout(1, 1, 36, 5, 1, 1)) - textLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP) - - textLayout:addChild(GUI.keyAndValue(1, 1, 0x696969, 0x969696, localization.nickname, ": " .. user.name)) - textLayout:addChild(GUI.keyAndValue(1, 1, 0x696969, 0x969696, "E-Mail", ": " .. user.email)) - textLayout:addChild(GUI.keyAndValue(1, 1, 0x696969, 0x969696, localization.registrationDate, ": " .. os.date("%d.%m.%Y", user.timestamp))) - textLayout.height = #textLayout.children * 2 - 1 - - local buttonsLayout = layout:addChild(newButtonsLayout(1, 1, layout.width, 2)) - - buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.changePassword)).onTouch = function() - addAccountShit(false, false, true) - end - - buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 2, 0, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.exit)).onTouch = function() - logout() - end - - if #result > 0 then - layout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.publications)) - - local comboBox = layout:addChild(GUI.comboBox(1, 1, 36, 1, 0xFFFFFF, 0x696969, 0x969696, 0xE1E1E1)) - for i = 1, #result do - comboBox:addItem(result[i].publication_name) - end - - local buttonsLayout = layout:addChild(newButtonsLayout(1, 1, layout.width, 2)) - buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.open)).onTouch = function() - newPublicationInfo(result[comboBox.selectedItem].file_id) - end - - local function editOrDelete(edit) - activity(true) - - local result = fieldAPIRequest("result", "publication", { - file_id = result[comboBox.selectedItem].file_id, - language_id = config.language_id, - }) - - if result then - if edit then - editPublication(result) + local child + for i = 1, #iconsContainer.children do + child = iconsContainer.children[i] + + child.moveX, child.moveY = child.moveX + child.forceX, child.moveY + child.forceY + + if child.forceX > 0 then + if child.forceX > overviewForceLimit then + child.forceX = child.forceX - overviewForceDecay else - deletePublication(result) + child.forceX = overviewForceLimit + end + else + if child.forceX < -overviewForceLimit then + child.forceX = child.forceX + overviewForceDecay + else + child.forceX = -overviewForceLimit end end - activity() + if child.forceY > 0 then + if child.forceY > overviewForceLimit then + child.forceY = child.forceY - overviewForceDecay + else + child.forceY = overviewForceLimit + end + else + if child.forceY < -overviewForceLimit then + child.forceY = child.forceY + overviewForceDecay + else + child.forceY = -overviewForceLimit + end + end + + if child.moveX <= 1 then + child.forceX, child.moveX = -child.forceX, 1 + elseif child.moveX + child.width - 1 >= iconsContainer.width then + child.forceX, child.moveX = -child.forceX, iconsContainer.width - child.width + 1 + end + + if child.moveY <= 1 then + child.forceY, child.moveY = -child.forceY, 1 + elseif child.moveY + child.height - 1 >= iconsContainer.height then + child.forceY, child.moveY = -child.forceY, iconsContainer.height - child.height + 1 + end + + child.localX, child.localY = math.floor(child.moveX), math.floor(child.moveY) end - buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.remove)).onTouch = function() - editOrDelete(false) - end - - buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.edit)).onTouch = function() - editOrDelete(true) - end + workspace:draw() + + return true end end - activity() - else - addAccountShit(true, false, false) - MineOSInterface.application:draw() - end - end + iconsContainer.eventHandler = function(workspace, object, e1, e2, e3, e4) + if e1 == "touch" or e1 == "drag" then + local child, deltaX, deltaY, vectorLength + for i = 1, #iconsContainer.children do + child = iconsContainer.children[i] + + deltaX, deltaY = e3 - child.x, e4 - child.y + vectorLength = math.sqrt(deltaX ^ 2 + deltaY ^ 2) + if vectorLength > 0 then + child.forceX = deltaX / vectorLength * math.random(overviewMaximumTouchAcceleration) + child.forceY = deltaY / vectorLength * math.random(overviewMaximumTouchAcceleration) + end + end + end - menuList:addItem(localization.statistics).onTouch = function() - statistics() - end + tick() + end - if user.token then - menuList:addItem(localization.messages).onTouch = function() - if messageToUser then - dialogGUI(messageToUser) - messageToUser = nil - else - dialogs() + local function makeBlyad(object) + object.localX = math.random(1, iconsContainer.width - object.width + 1) + object.localY = math.random(1, iconsContainer.height - object.width + 1) + object.moveX = object.localX + object.moveY = object.localY + object.forceX = math.random(-100, 100) / 100 * overviewForceLimit + object.forceY = math.random(-100, 100) / 100 * overviewForceLimit + + if not tick() then + workspace:draw() + end + end + + for i = 2, #publications do + makeBlyad(iconsContainer:addChild(GUI.image(1, 1, getPublicationIcon(publications[i])), 1)) end end end +end - menuList:addItem(localization.account).onTouch = function() - account() - end - - menuList:addItem(localization.settings).onTouch = function() +local function settings() + local function logout() + user = {} + saveUser() settings() end - - menuList.selectedItem = menuID - menuList:getItem(menuList.selectedItem).onTouch() + + local function addAccountShit(login, register, recover) + contentContainer:removeChildren() + + local layout = contentContainer:addChild(GUI.layout(1, 1, contentContainer.width, contentContainer.height, 1, 1)) + + layout:addChild(GUI.label(1, 1, 36, 1, 0x0, login and localization.login or register and localization.createAccount or recover and localization.changePassword)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + local nameInput = layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", localization.nickname)) + local emailInput = layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", login and localization.nicknameOrEmail or "E-mail")) + local currentPasswordInput = layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", localization.currentPassword, false, "*")) + local passwordInput = layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", recover and localization.newPassword or localization.password, false, "*")) + + local singleSessionSwitchAndLabel = layout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xC3C3C3, 0xFFFFFF, 0xA5A5A5, localization.singleSession .. ":", config.singleSession)) + + layout:addChild(GUI.button(1, 1, 36, 3, 0xA5A5A5, 0xFFFFFF, 0x696969, 0xFFFFFF, "OK")).onTouch = function() + if login then + local result = fieldAPIRequest("result", "login", { + [(string.find(emailInput.text, "@") and "email" or "name")] = emailInput.text, + password = passwordInput.text + }) + + if result then + user = { + token = result.token, + name = result.name, + id = result.id, + email = result.email, + timestamp = result.timestamp, + } + + settings() + + config.singleSession = singleSessionSwitchAndLabel.switch.state + if not config.singleSession then + saveUser() + end + saveConfig() + end + elseif register then + local result = fieldAPIRequest("result", "register", { + name = nameInput.text, + email = emailInput.text, + password = passwordInput.text, + }) + + if result then + showLabelAsContent(contentContainer, localization.registrationSuccessfull) + end + else + local success, reason = RawAPIRequest("change_password", { + email = user.email, + current_password = currentPasswordInput.text, + new_password = passwordInput.text, + }) + + if success then + logout() + else + GUI.alert(reason) + end + end + end + + if login then + local registerLayout = layout:addChild(GUI.layout(1, 1, layout.width, 1, 1, 1)) + registerLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL) + registerLayout:addChild(GUI.text(1, 1, 0xA5A5A5, localization.notRegistered)) + registerLayout:addChild(GUI.adaptiveButton(1, 1, 0, 0, nil, 0x696969, nil, 0x0, localization.createAccount)).onTouch = function() + addAccountShit(false, true, false) + end + end + + currentPasswordInput.hidden = not recover + emailInput.hidden = recover + nameInput.hidden = login or recover + singleSessionSwitchAndLabel.hidden = not login + end + + messagesItem.hidden = not user.token + + if user.token then + local result = fieldAPIRequest("result", "publications", { + user_name = user.name, + order_by = "name", + order_direction = "asc", + }) + + if result then + contentContainer:removeChildren() + local layout = contentContainer:addChild(GUI.layout(1, 1, contentContainer.width, contentContainer.height, 1, 1)) + + layout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.profile)) + + local textLayout = layout:addChild(GUI.layout(1, 1, 36, 5, 1, 1)) + textLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP) + + textLayout:addChild(GUI.keyAndValue(1, 1, 0x696969, 0x969696, localization.nickname, ": " .. user.name)) + textLayout:addChild(GUI.keyAndValue(1, 1, 0x696969, 0x969696, "E-Mail", ": " .. user.email)) + textLayout:addChild(GUI.keyAndValue(1, 1, 0x696969, 0x969696, localization.registrationDate, ": " .. os.date("%d.%m.%Y", user.timestamp))) + textLayout.height = #textLayout.children * 2 - 1 + + local buttonsLayout = layout:addChild(newButtonsLayout(1, 1, layout.width, 2)) + + buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.changePassword)).onTouch = function() + addAccountShit(false, false, true) + end + + buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 2, 0, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.exit)).onTouch = function() + logout() + end + + buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 2, 0, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.clearCache)).onTouch = function() + local list = filesystem.list(iconCachePath) + for i = 1, #list do + filesystem.remove(iconCachePath .. list[i]) + end + end + + layout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.language)) + + local languageComboBox = layout:addChild(GUI.comboBox(1, 1, 36, 1, 0xFFFFFF, 0x696969, 0x969696, 0xE1E1E1)) + for key, value in pairs(languages) do + languageComboBox:addItem(value).onTouch = function() + config.language_id = key + saveConfig() + end + + if key == config.language_id then + languageComboBox.selectedItem = languageComboBox:count() + end + end + + if #result > 0 then + layout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.publications)) + + local comboBox = layout:addChild(GUI.comboBox(1, 1, 36, 1, 0xFFFFFF, 0x696969, 0x969696, 0xE1E1E1)) + for i = 1, #result do + comboBox:addItem(result[i].publication_name) + end + + local buttonsLayout = layout:addChild(newButtonsLayout(1, 1, layout.width, 2)) + buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.open)).onTouch = function() + newPublicationInfo(result[comboBox.selectedItem].file_id) + end + + buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0xC3C3C3, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.edit)).onTouch = function() + local result = fieldAPIRequest("result", "publication", { + file_id = result[comboBox.selectedItem].file_id, + language_id = config.language_id, + }) + + if result then + editPublication(result) + end + end + end + end + else + addAccountShit(true, false, false) + workspace:draw() + end +end + +local function dialogGUI(to_user_name) + local messages + if to_user_name then + local result = fieldAPIRequest("result", "messages", { + token = user.token, + user_name = to_user_name + }) + + if result then + messages = result + end + end + messages = messages or {} + + contentContainer:removeChildren() + + local button = contentContainer:addChild(GUI.adaptiveButton(1, contentContainer.height - 2, 2, 1, 0x4B4B4B, 0xE1E1E1, 0x2D2D2D, 0xE1E1E1, localization.send)) + button.localX = contentContainer.width - button.width + 1 + button.colors.disabled.background = 0xB4B4B4 + button.colors.disabled.text = 0xFFFFFF + button.disabled = true + + local input = contentContainer:addChild(GUI.input(1, button.localY, contentContainer.width - button.width, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "", localization.typeMessageText)) + + local function check() + button.disabled = not (to_user_name and #input.text > 0) + end + + input.onInputFinished = check + + button.onTouch = function() + local success, reason = RawAPIRequest("message", { + token = user.token, + user_name = to_user_name, + text = input.text + }) + + if success then + dialogGUI(to_user_name) + else + GUI.alert(reason) + end + end + + local messagesContainer = contentContainer:addChild(GUI.container(1, 4, contentContainer.width, contentContainer.height - 6)) + messagesContainer.eventHandler = containerScrollEventHandler + + local panel = contentContainer:addChild(GUI.panel(1, 1, contentContainer.width, 3, 0xFFFFFF)) + if not to_user_name then + panel.colors.transparency = nil + local text = contentContainer:addChild(GUI.text(3, 2, 0x0, localization.toWho)) + local input = contentContainer:addChild(GUI.input(text.localX + text.width, 1, contentContainer.width - text.width - 4, 3, 0xFFFFFF, 0x878787, 0xC3C3C3, 0xFFFFFF, 0x878787, to_user_name or "", localization.typeUserName)) + input.onInputFinished = function() + to_user_name = input.text + check() + end + else + local keyAndValue = contentContainer:addChild(GUI.keyAndValue(1, 2, 0x878787, 0x0, localization.dialogWith, to_user_name)) + keyAndValue.localX = math.floor(contentContainer.width / 2 - (keyAndValue.keyLength + keyAndValue.valueLength) / 2) + -- contentContainer:addChild(GUI.label(1, 2, contentContainer.width, 1, 0x2D2D2D, to_user_name)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_CENTER) + end + + local function cloudDraw(object) + local backgroundColor, textColor = object.me and 0x6692FF or 0xFFFFFF, object.me and 0xFFFFFF or 0x4B4B4B + + screen.drawRectangle(object.x, object.y, object.width, object.height, backgroundColor, textColor, " ") + screen.drawText(object.x, object.y - 1, backgroundColor, "⢀" .. string.rep("⣀", object.width - 2) .. "⡀") + screen.drawText(object.x, object.y + object.height, backgroundColor, "⠈" .. string.rep("⠉", object.width - 2) .. "⠁") + + local date = os.date("%d.%m.%Y, %H:%M", object.timestamp) + if object.me then + screen.drawText(object.x - #date - 1, object.y, 0xC3C3C3, date) + screen.drawText(object.x + object.width, object.y + object.height - 1, backgroundColor, "◤") + else + screen.drawText(object.x + object.width + 1, object.y, 0xC3C3C3, date) + screen.drawText(object.x - 1, object.y + object.height - 1, backgroundColor, "◥") + end + + for i = 1, #object.lines do + screen.drawText(object.x + 1, object.y + i - 1, textColor, object.lines[i]) + end + end + + local function newCloud(y, width, cloudText, me, timestamp) + local lines = text.wrap(cloudText, width - 2) + local object = GUI.object(me and messagesContainer.width - width - 1 or 3, y - #lines + 1, width, 1) + + object.lines = lines + object.height = #lines + object.me = me + object.text = cloudText + object.draw = cloudDraw + object.timestamp = timestamp + + return object + end + + local y = messagesContainer.height - 1 + for j = 1, #messages do + local cloud = messagesContainer:addChild(newCloud(y, math.floor(messagesContainer.width * 0.6), tostring(messages[j].text), messages[j].user_name == user.name, messages[j].timestamp), 1) + y = y - cloud.height - 2 + end + + -- Пустой объект для прокрутки ниже этой прозрачной пизды + messagesContainer:addChild(GUI.object(1, y, 1, 2), 1) +end + +local function dialogs() + local dialogs = fieldAPIRequest("result", "dialogs", { + token = user.token, + }) + + if dialogs then + contentContainer:removeChildren() + + local dialogsContainer = contentContainer:addChild(GUI.container(1, 1, contentContainer.width, contentContainer.height)) + dialogsContainer.eventHandler = containerScrollEventHandler + + local sendMessageButton = dialogsContainer:addChild(GUI.adaptiveRoundedButton(1, #dialogs > 0 and 2 or math.floor(dialogsContainer.height / 2 + 1), 2, 0, 0x696969, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.newMessage)) + sendMessageButton.localX = math.floor(dialogsContainer.width / 2 - sendMessageButton.width / 2) + sendMessageButton.onTouch = function() + dialogGUI() + end + + if #dialogs > 0 then + local y = sendMessageButton.localY + 2 + + for i = 1, #dialogs do + local backgroundColor, nicknameColor, timestampColor, textColor = 0xFFFFFF, 0x0, 0xD2D2D2, 0x969696 + if dialogs[i].last_message_is_read == 0 and dialogs[i].last_message_user_name ~= user.name then + backgroundColor, nicknameColor, timestampColor, textColor = 0xCCDBFF, 0x0, 0xB4B4B4, 0x878787 + end + + local dialogContainer = dialogsContainer:addChild(GUI.container(3, y, dialogsContainer.width - 4, 4)) + addPanel(dialogContainer,backgroundColor) + + dialogContainer:addChild(GUI.keyAndValue(3, 2, nicknameColor, timestampColor, dialogs[i].dialog_user_name, os.date(" (%d.%m.%Y, %H:%M)", dialogs[i].timestamp))) + dialogContainer:addChild(GUI.text(3, 3, textColor, text.limit((dialogs[i].last_message_user_name == user.name and localization.yourText .. " " or "") .. dialogs[i].text, dialogContainer.width - 4, "right"))) + + dialogContainer.eventHandler = function(workspace, object, e1) + if e1 == "touch" then + dialogContainer.panel.colors.background = 0xE1E1E1 + dialogGUI(dialogs[i].dialog_user_name) + end + end + + y = y + dialogContainer.height + 1 + end + else + dialogsContainer:addChild(GUI.label(1, sendMessageButton.localY - 2, dialogsContainer.width, 1, 0xA5A5A5, localization.hereBeYourDialogs)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_CENTER) + end + end end newPublicationInfo = function(file_id) lastMethod, lastArguments = newPublicationInfo, {file_id} - activity(true) local publication = fieldAPIRequest("result", "publication", { file_id = file_id, @@ -1190,7 +1122,7 @@ newPublicationInfo = function(file_id) }) if publication then - MineOSInterface.application:draw() + workspace:draw() local reviews = fieldAPIRequest("result", "reviews", { file_id = file_id, @@ -1261,7 +1193,7 @@ newPublicationInfo = function(file_id) end else buttonsLayout:addChild(GUI.adaptiveRoundedButton(2, 1, 1, 0, 0xA5A5A5, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.newMessageToDeveloper)).onTouch = function() - mainMenu(2, publication.user_name) + dialogGUI(publication.user_name) end local existingReviewText @@ -1275,7 +1207,7 @@ newPublicationInfo = function(file_id) end buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0xA5A5A5, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, existingReviewText and localization.changeReview or localization.writeReview)).onTouch = function() - local container = MineOSInterface.addBackgroundContainer(window, existingReviewText and localization.changeReview or localization.writeReview) + local container = GUI.addBackgroundContainer(workspace, true, true, existingReviewText and localization.changeReview or localization.writeReview) local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, existingReviewText or "", localization.writeReviewHere)) @@ -1284,10 +1216,10 @@ newPublicationInfo = function(file_id) pizda.width = eblo.width + 9 local cyka = pizda:addChild(newRatingWidget(eblo.width + 1, 1, 4)) - cyka.eventHandler = function(application, object, e1, e2, e3) + cyka.eventHandler = function(workspace, object, e1, e2, e3) if e1 == "touch" then - cyka.rating = math.round((e3 - object.x + 1) / object.width * 5) - MineOSInterface.application:draw() + cyka.rating = number.round((e3 - object.x + 1) / object.width * 5) + workspace:draw() end end @@ -1296,8 +1228,6 @@ newPublicationInfo = function(file_id) govno.colors.disabled.background = 0xA5A5A5 govno.colors.disabled.text = 0xC3C3C3 govno.onTouch = function() - activity(true) - local success, reason = RawAPIRequest("review", { token = user.token, file_id = publication.file_id, @@ -1306,15 +1236,13 @@ newPublicationInfo = function(file_id) }) container:remove() - MineOSInterface.application:draw() + workspace:draw() if success then newPublicationInfo(publication.file_id) else GUI.alert(reason) end - - activity() end input.onInputFinished = function() @@ -1328,7 +1256,7 @@ newPublicationInfo = function(file_id) end end - MineOSInterface.application:draw() + workspace:draw() end input.onInputFinished() @@ -1348,8 +1276,8 @@ newPublicationInfo = function(file_id) y = y + 2 end - local function addTextBox(text) - local lines = string.wrap(text, textDetailsContainer.width - 4) + local function addTextBox(t) + local lines = text.wrap(t, textDetailsContainer.width - 4) local textBox = textDetailsContainer:addChild(GUI.textBox(3, y, textDetailsContainer.width - 4, #lines, nil, 0x969696, lines, 1, 0, 0)) textBox.eventHandler = nil y = y + textBox.height + 1 @@ -1424,7 +1352,7 @@ newPublicationInfo = function(file_id) reviewContainer:addChild(newRatingWidget(3, y, reviews[i].rating)) y = y + 1 - local lines = string.wrap(tostring(reviews[i].comment), reviewContainer.width - 4) + local lines = text.wrap(tostring(reviews[i].comment), reviewContainer.width - 4) local textBox = reviewContainer:addChild(GUI.textBox(3, y, reviewContainer.width - 4, #lines, nil, 0x878787, lines, 1, 0, 0)) textBox.eventHandler = nil y = y + #lines @@ -1446,8 +1374,6 @@ newPublicationInfo = function(file_id) layout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP) local function go(rating) - activity(true) - local success = fieldAPIRequest("result", "review_vote", { token = user.token, review_id = reviews[i].id, @@ -1459,8 +1385,6 @@ newPublicationInfo = function(file_id) wasHelpText.color = 0x696969 layout:remove() end - - activity() end layout:addChild(GUI.adaptiveButton(1, 1, 0, 0, nil, 0x696969, nil, 0x2D2D2D, localization.yes)).onTouch = function() @@ -1472,7 +1396,7 @@ newPublicationInfo = function(file_id) end layout:addChild(GUI.text(1, 1, 0xC3C3C3, "|")) layout:addChild(GUI.adaptiveButton(1, 1, 0, 0, nil, 0x696969, nil, 0x2D2D2D, localization.newMessagePersonal)).onTouch = function() - mainMenu(2, reviews[i].user_name) + dialogGUI(reviews[i].user_name.user_name) end y = y + 1 @@ -1487,8 +1411,6 @@ newPublicationInfo = function(file_id) layout.height = layout.children[#layout.children].localY + layout.children[#layout.children].height - 1 end end - - activity() end -------------------------------------------------------------------------------- @@ -1517,7 +1439,7 @@ local function newPlusMinusCyka(width, disableLimit) layout.removeButton.onTouch = function() layout.comboBox:removeItem(layout.comboBox.selectedItem) - MineOSInterface.application:draw() + workspace:draw() end return layout @@ -1616,7 +1538,7 @@ editPublication = function(initialPublication, initialCategoryID) local lastDependencyType = 1 dependenciesLayout.addButton.onTouch = function() - local container = MineOSInterface.addBackgroundContainer(MineOSInterface.application, localization.addDependency) + local container = GUI.addBackgroundContainer(workspace, true, true, localization.addDependency) local dependencyTypeComboBox = container.layout:addChild(GUI.comboBox(1, 1, 36, 3, 0xFFFFFF, 0x4B4B4B, 0x969696, 0xE1E1E1)) dependencyTypeComboBox:addItem(localization.fileByURL) @@ -1637,7 +1559,7 @@ editPublication = function(initialPublication, initialCategoryID) }) container:remove() - MineOSInterface.application:draw() + workspace:draw() end publicationNameInput.onInputFinished = function() @@ -1659,7 +1581,7 @@ editPublication = function(initialPublication, initialCategoryID) dependencyTypeComboBox.onItemSelected = function() onDependencyTypeComboBoxItemSelected() - MineOSInterface.application:draw() + workspace:draw() end pathType.switch.onStateChanged = function() @@ -1669,7 +1591,7 @@ editPublication = function(initialPublication, initialCategoryID) publicationNameInput.onInputFinished() onDependencyTypeComboBoxItemSelected() pathType.switch.onStateChanged() - MineOSInterface.application:draw() + workspace:draw() end local publishButton = layout:addChild(GUI.adaptiveRoundedButton(1, 1, 2, 0, 0x696969, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.save)) @@ -1688,7 +1610,7 @@ editPublication = function(initialPublication, initialCategoryID) mainPathInput.hidden = pathHint.hidden nameInput.onInputFinished() - MineOSInterface.application:draw() + workspace:draw() end categoryComboBox.onItemSelected() @@ -1705,8 +1627,6 @@ editPublication = function(initialPublication, initialCategoryID) path = "Icon.pic" }) end - - activity(true) local success, reason = RawAPIRequest(initialPublication and "update" or "upload", { -- Вот эта хня чисто для апдейта @@ -1724,7 +1644,7 @@ editPublication = function(initialPublication, initialCategoryID) }) if success then - window.tabBar.selectedItem = categoryComboBox.selectedItem + 1 + leftList.selectedItem = categoryComboBox.selectedItem + 1 if initialPublication then newPublicationInfo(initialPublication.file_id) @@ -1736,18 +1656,13 @@ editPublication = function(initialPublication, initialCategoryID) else GUI.alert(reason) end - - activity() end - - activity() end -------------------------------------------------------------------------------- updateFileList = function(category_id, updates) lastMethod, lastArguments = updateFileList, {category_id, updates} - activity(true) local file_ids if updates then @@ -1790,14 +1705,14 @@ updateFileList = function(category_id, updates) if updates then if #result > 0 then layout:addChild(GUI.adaptiveRoundedButton(1, 1, 2, 0, 0x696969, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, localization.updateAll)).onTouch = function() - local container = MineOSInterface.addBackgroundContainer(MineOSInterface.application, "") + local container = GUI.addBackgroundContainer(workspace, true, true, "") local progressBar = container.layout:addChild(GUI.progressBar(1, 1, 40, 0x66DB80, 0x0, 0xE1E1E1, 0, true, true, "", "%")) for i = 1, #result do container.label.text = localization.downloading .. " " .. result[i].publication_name - progressBar.value = math.round(i / #result * 100) - MineOSInterface.application:draw() + progressBar.value = number.round(i / #result * 100) + workspace:draw() local publication = fieldAPIRequest("result", "publication", { file_id = result[i].file_id, @@ -1813,7 +1728,7 @@ updateFileList = function(category_id, updates) local dependency = publication.dependencies_data[publication.all_dependencies[j]] if not dependency.publication_name then container.label.text = localization.downloading .. " " .. dependency.path - MineOSInterface.application:draw() + workspace:draw() if getUpdateState(publication.all_dependencies[j], dependency.version) < 4 then local dependencyPath = getDependencyPath(fileVersions[publication.file_id].path, dependency) @@ -1825,7 +1740,7 @@ updateFileList = function(category_id, updates) tryToDownload(dependency.source_url, dependencyPath) else - os.sleep(0.05) + event.sleep(0.05) end end end @@ -1839,7 +1754,7 @@ updateFileList = function(category_id, updates) end end else - local input = layout:addChild(GUI.input(1, 1, 20, layout.height, 0xFFFFFF, 0x2D2D2D, 0x696969, 0xFFFFFF, 0x2D2D2D, search or "", localization.search, true)) + local input = layout:addChild(GUI.input(1, 1, 20, layout.height, 0xFFFFFF, 0x2D2D2D, 0xA5A5A5, 0xFFFFFF, 0x2D2D2D, search or "", localization.search, true)) input.onInputFinished = function() if #input.text == 0 then search = nil @@ -1920,45 +1835,50 @@ updateFileList = function(category_id, updates) end counter = counter + 1 - MineOSInterface.application:draw() + workspace:draw() end else showLabelAsContent(contentContainer, localization.noUpdates) end end - - activity() end -local function loadCategory(category_id, updates) +local function loadCategory(...) currentPage, search = 0, nil - updateFileList(category_id, updates) + updateFileList(...) end -------------------------------------------------------------------------------- -window.tabBar:addItem(localization.categoryOverview).onTouch = function() - mainMenu(1) -end +leftList:addItem(localization.categoryOverview).onTouch = overview for i = 1, #categories do - window.tabBar:addItem(categories[i]).onTouch = function() + leftList:addItem(categories[i]).onTouch = function() loadCategory(i) end end -window.tabBar:addItem(localization.categoryUpdates).onTouch = function() +leftList:addItem(localization.categoryUpdates).onTouch = function() loadCategory(nil, true) end -window.onResize = function(width, height) - window.backgroundPanel.width = width - window.backgroundPanel.height = height - 3 - contentContainer.width = width - contentContainer.height = window.backgroundPanel.height - window.tabBar.width = width +messagesItem = leftList:addItem(localization.messages) +messagesItem.onTouch = dialogs - progressIndicator.localX = window.width - progressIndicator.width +leftList:addItem(localization.settings).onTouch = settings + +window.onResize = function(width, height) + leftList.height = height - leftListPanel.height + + window.backgroundPanel.localX = leftList.width + 1 + window.backgroundPanel.width = width - leftList.width + window.backgroundPanel.height = height + + contentContainer.localX = window.backgroundPanel.localX + contentContainer.width = window.backgroundPanel.width + contentContainer.height = window.backgroundPanel.height + + progressIndicator.localY = height - progressIndicator.height appsPerWidth = math.floor((contentContainer.width + appHSpacing) / (appWidth + appHSpacing)) appsPerHeight = math.floor((contentContainer.height - 6 + appVSpacing) / (appHeight + appVSpacing)) @@ -1973,11 +1893,14 @@ end loadConfig() +lastMethod = loadCategory + if args[1] == "updates" then - lastMethod, lastArguments = updateFileList, {nil, true} - window.tabBar.selectedItem = #categories + 2 + lastArguments = {nil, true} + leftList.selectedItem = #categories + 2 else - lastMethod, lastArguments = mainMenu, {1} + lastArguments = {1} + leftList.selectedItem = 2 end window:resize(window.width, window.height) \ No newline at end of file diff --git a/Applications/AppMarket/Icon.pic b/Applications/AppMarket/Icon.pic deleted file mode 100644 index 1e0deecfa632d0f9ab14ada7d678dd78b68790d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 zcmXYp%L#xm5JWS(o6VMCFM{C7(mY5I1UpDO>OGC zb^mUTjjz9xvFP#A2{M^%h#Q=7%hh!_nQnaZH8H+HaM$Nr7o#`7_Js$E(U-(hpPDOP zroR2RmLntVA{gKO7QAG}2syhXg4`g#>MN<z3^FUHH2H#wr5 z$|wrSCcgQWpj`3Qsc)VgnPPnN3Lnc3A@|K=3<0 zRx>`~LzU>t*B|rHY#A~oxB;8;l&Ry+6R3ycp}>7A5<|+lfZ9nDlCpbne^V zWRO&!&oWH?9;rnk3}87#dbmkW*cQIizH3=&HS9X^-OuKqJ(F#TSV?Y>SVUwucZ9yZ z%@!y|BZiTTnjhGlB68H6Q|z4NYu`PgV3mzi70l?BV4N{gF=H}{C$=U|+1dAx+;?m+ zF^_Y~up?`YB4|S+vyIw^U2PfXbbvD)=#u)D$}$zjZYPpTb($en=`-vnGB(#GL72fz zcsCgmZFH=%a}v@aKQvo;NzA9(gXM;W3Zl#Yk-Ja%{|s?^&2@h zyu^vGKc~9YYeuo}?)C%;wSmv=eH;vEPzVhyfnXWa3@2|;tb+9Kyi{k*nu3Ym?=2#e zAouN;^jGD5s0YvHD%lWA-~Nv>QD1Unyx>vj#5XaP2?JzWk@f8ZrdRt)eEqID@283# zX3W%CZCw14T{<~KAxW+*82jeCK6A=F?N`U;$hS&*jTHO(9oBfD#3057;V2p;zuWUT z`h@b|?P(oic()d^+XuABqC6Q-_F*_IMnV5O8gB9NX>z=$R{zZed)M#JkI{-hWGMfX z-`A~Ly`)xx`6ffC99Zr$tQ^l#sC_@;Li?8b=5r+kp;{3yF$*Qlw&wy5-reGRI^*bj zACOT>l+A>fUBwHLe@HNukJ}JouNlk-YocT6$k1*D>MZo0j~Gk0ammTtr>E^}e>Lpf z*N-{UbtGp)&ce4(C|fn)(*QfQgiw3SLa3=kENwK=?%eDLTZT;ae5^h2?epkGUwcWq zkt*|-(lxn$748iOM6AIQwzdBx75=YK0+Ts-fn~piZ6VC7YA4g}J zX<S+SVPy zTXApNTd3G@fQ=%$(;N8+M;UH?`}8=WgG+AU9D6pH7^d0~tLE{bZManrYWrTXiCF{BPsT5I>ns;OIS0{!mLhG&Br zt<)zik^qr!=>=?BhP9rIZGR-^M%A0>=-fuwZ^@!Kgk2b=%Ua1f*I? s[j][1]-2 and y > s[j][2]-2 and y < s[j][2]+2) then - x = math.random(var, var+19) - y = math.random(3, 12) - break - elseif i ~= j and (j == 10 or (i == 10 and j == 9)) then - s[i][1] = makePixelX(x, var) - s[i][2] = y - yes = false - end - end - end - end - end - if var == 25 then - ships = s - else - shipsE = s - end -end - ---Рисуем поле -function drawField() - g.setBackground(0xffffff) - g.fill(25,3,20,10," ") - g.setBackground(0xDDDDDD) - if args[1] ~= "fast" then - for i=1,10 do - local delta = math.fmod(i,2) - for j=1,5 do - g.fill(23+4*j-2*delta, i+2, 2, 1, " ") - end - end - end -end - ---Рисуем поле врага -function drawFieldE() - g.setBackground(0xffffff) - g.fill(3,3,20,10," ") - g.setBackground(0xDDDDDD) - if args[1] ~= "fast" then - for i=1,10 do - local delta = math.fmod(i,2) - for j=1,5 do - g.fill(1+4*j-2*delta, i+2, 2, 1, " ") - end - end - end -end - ---Кнопка готово после рандома -function drawButton2() - g.setBackground(colors.pink) - term.setCursor(13, 11) - term.write(" Готово ") -end - ---Кнопка рандома своих кораблей -function drawButton() - g.setBackground(colors.lime) - term.setCursor(3, 11) - term.write(" Авто ") -end - ---Очищаем пустое место -function clearShipsField() - g.setBackground(colors.lightGray) - g.fill(3,3,22,10," ") -end - ---Гуишечка -drawField() -drawShips() -drawButton() -g.setBackground(colors.lightGray) -g.setForeground(colors.black) -term.setCursor(3,13) -term.write("Установите корабли") - ---Цикл для установки своих корабликов вручную -local ship = 0 -local prevX = 0 -local shipCoords = {0,0} -local setting = true -local playing = true -local button2 = false -while setting do - local event, _, x, y = event.pull() - if event == "touch" then - if x > 2 and x < 13 and y == 11 then - setShipsAuto(25) - drawField() - clearShipsField() - drawShips() - drawButton() - drawButton2() - button2 = true - elseif button2 and x > 12 and x < 24 and y == 11 then - setting = false - break - elseif x > w-4 and x < w and y == 1 then - setting = false - playing = false - break - end - elseif event == "drag" then - if ship == 0 then - for i=1,10 do - if x > ships[i][1] and x < ships[i][1]+ships[i][3] and y == ships[i][2] then - ship = i - shipCoords[1] = ships[i][1] - shipCoords[2] = ships[i][2] - break - end - end - else - ships[ship][1] = ships[ship][1] + x - prevX - ships[ship][2] = y - if ships[ship][1] > 2 and ships[ship][1]+ships[ship][3]-1 < 45 and y > 2 and y < 13 then - drawField() - clearShipsField() - drawShips() - drawButton() - end - end - prevX = x - elseif event == "drop" then - if ship > 0 then - if ships[ship][1] < 25 or ships[ship][1]+ships[ship][3]-1 > 45 or y < 3 or y > 13then - ships[ship][1] = shipCoords[1] - ships[ship][2] = shipCoords[2] - end - for i=1,10 do - if i ~= ship and (ships[ship][1] < ships[i][1]+ships[i][3]+1 and ships[ship][1]+ships[ship][3]-1 > ships[i][1]-2 and ships[ship][2] > ships[i][2]-2 and ships[ship][2] < ships[i][2]+2) then - ships[ship][1] = shipCoords[1] - ships[ship][2] = shipCoords[2] - break - end - end - ships[ship][1] = makePixelX(ships[ship][1], 25) - end - ship = 0 - drawField() - clearShipsField() - drawShips() - drawButton() - for i=1,10 do - if ships[i][1] < 25 then - break - elseif i == 10 then - setting = false - break - end - end - end -end - ---Следующий цикл для игры -setShipsAuto(3) -drawFieldE() -g.setBackground(colors.lightGray) -g.setForeground(colors.black) -term.setCursor(3,13) -term.write("Противник ") -term.setCursor(25,13) -term.write("Вы") -g.setBackground(colors.magenta) -g.fill(23, 3, 2, 10, " ") -while playing do - local event, _, x, y = event.pull() - if event == "touch" then - if shots < 20 and shotsE2 < 20 and x > 2 and x < 23 and y > 2 and y < 13 then - x = makePixelX(x, 3) - for i=1,10 do - if x > shipsE[i][1]-1 and x < shipsE[i][1]+shipsE[i][3] and y == shipsE[i][2] then - shots = shots + 1 - g.setBackground(colors.red) - break - end - g.setBackground(colors.blue) - end - g.fill(x, y, 2, 1, " ") - local yes = true - local xE, yE = 0, 0 - while yes do - xE = makePixelX(math.random(25,44), 3) - yE = math.random(3,12) - for i=1,#shotsE do - if xE == shotsE[i][1] and yE == shotsE[i][2] then - break - elseif i == #shotsE then - yes = false - break - end - end - end - table.insert(shotsE, {makePixelX(xE, 3), yE}) - if args[2] ~= "notime" then - g.setBackground(colors.purple) - g.fill(23, 3, 2, 10, " ") - os.sleep(math.floor(math.random(2))-0.5) - g.setBackground(colors.magenta) - g.fill(23, 3, 2, 10, " ") - end - for i=1,10 do - if xE > ships[i][1]-1 and xE < ships[i][1]+ships[i][3] and yE == ships[i][2] then - shotsE2 = shotsE2 + 1 - g.setBackground(colors.red) - break - end - g.setBackground(colors.blue) - end - g.fill(xE, yE, 2, 1, " ") - if shots == 20 or shotsE2 == 20 then - g.setBackground(colors.lightGray) - g.fill(2, 3, 43, 12, " ") - g.setBackground(colors.white) - g.fill(15, 5, 16, 3, " ") - - if shots == 20 then - term.setCursor(20, 6) - term.write("Победа") - elseif shotsE2 == 20 then - term.setCursor(18, 6) - term.write("Поражение") - end - end - elseif x > w-4 and x < w and y == 1 then - playing = false - break - end - end -end - ---При выходе -g.setForeground(colors.white) -g.setBackground(colors.black) -term.clear() -g.setResolution(g.maxResolution()) \ No newline at end of file diff --git a/Applications/Battleship/Icon.pic b/Applications/Battleship/Icon.pic deleted file mode 100644 index c7304d2780f42f3c1b44f8d7a6d259f1bf73d64b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmXZUu?>JQ3`9|%V>=#$U67Eds2B(-Go(SR75Xd!Mxndn-QVVQF&BPFdMzN{!6|9J mkrXWEyDv|ze>Xav0+~O6A-Zg diff --git a/Applications/Braille/About/English.txt b/Applications/Braille/About/English.txt deleted file mode 100644 index 7fc0af0d..00000000 --- a/Applications/Braille/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Program that allows to draw images using Braille chars simple and quickly. At this moment default Photoshop.app doesn't have support of this awesome feature. \ No newline at end of file diff --git a/Applications/Braille/About/Russian.txt b/Applications/Braille/About/Russian.txt deleted file mode 100644 index befd1fba..00000000 --- a/Applications/Braille/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Программа, позволяющая быстро и просто рисовать изображения символами шрифта Брайля. На данный момент служит альтернативой системному Photoshop, в котором отсутствует поддержка подобного функционала. \ No newline at end of file diff --git a/Applications/Braille/Icon.pic b/Applications/Braille/Icon.pic deleted file mode 100644 index a959182b8df53e7fd234e59c3bb313424ffb9397..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171 zcmXwy!41MN3`P5GC$YpBjDm#3iIqSc+A)%50&dLn7Xh~oCx1Ub>)$W8Yj(qTI picture[1] then - x, y = 1, y + 1 - end - end - - image.save(path, picture) - else - local pizda = { - width = drawingArea.width / 4, - height = drawingArea.height / 4, - } - - for i = 1, #drawingArea.children do - table.insert(pizda, { - background = drawingArea.children[i].background, - foreground = drawingArea.children[i].foreground, - pixels = drawingArea.children[i].pixels, - }) - end - - table.toFile(path, pizda, true) - end - end - - filesystemDialog:show() -end - -openButton.onTouch = function() - local filesystemDialog = GUI.addFilesystemDialog(application, true, 50, math.floor(application.height * 0.8), "OK", "Cancel", "Path", "/") - - filesystemDialog:setMode(GUI.IO_MODE_OPEN, GUI.IO_MODE_FILE) - filesystemDialog:addExtensionFilter(".braiile") - - filesystemDialog.onSubmit = function(path) - local pizda = table.fromFile(path) - drawingArea:removeChildren() - - newNoGUI(pizda.width, pizda.height) - - for i = 1, #drawingArea.children do - drawingArea.children[i].background = pizda[i].background - drawingArea.children[i].foreground = pizda[i].foreground - drawingArea.children[i].pixels = pizda[i].pixels - end - - application:draw() - end - - filesystemDialog:show() -end - -window.actionButtons.minimize:remove() -window.actionButtons.maximize:remove() - - ---------------------------------------------------------------------------------------------------------- - -newNoGUI(8, 4) -application:draw() - - - diff --git a/Applications/BufferDemo/About/English.txt b/Applications/BufferDemo/About/English.txt deleted file mode 100644 index 1d721885..00000000 --- a/Applications/BufferDemo/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Программа-демонстратор возможностей нашей библиотеки двойной буферизации изображения. Вся ОС работает именно на этой библиотеке. \ No newline at end of file diff --git a/Applications/BufferDemo/About/Russian.txt b/Applications/BufferDemo/About/Russian.txt deleted file mode 100644 index 1d721885..00000000 --- a/Applications/BufferDemo/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Программа-демонстратор возможностей нашей библиотеки двойной буферизации изображения. Вся ОС работает именно на этой библиотеке. \ No newline at end of file diff --git a/Applications/BufferDemo/BufferDemo.lua b/Applications/BufferDemo/BufferDemo.lua deleted file mode 100755 index 7f72a2b9..00000000 --- a/Applications/BufferDemo/BufferDemo.lua +++ /dev/null @@ -1,160 +0,0 @@ - -local buffer = require("doubleBuffering") -local event = require("event") -local image = require("image") - -local currentBackground = 0x990000 -local risovatKartinku = true -local showPanel = true -local transparency = 0.25 -local xWindow, yWindow = 5, 5 - -local fon = image.load("MineOS/Applications/BufferDemo.app/Resources/Wallpaper.pic") - -buffer.flush() -local bufferWidth, bufferHeight = buffer.getResolution() - -local function drawBackground() - --Заполним весь наш экран цветом фона 0x262626, цветом текста 0xFFFFFF и символом " " - if not risovatKartinku then - buffer.square(1, 1, bufferWidth, bufferHeight, currentBackground, 0xFFFFFF, " ") - else - buffer.image(1, 1, fon) - end -end - ---Создаем переменные с координатами начала и размерами нашего окна -local width, height = 82, 25 - --- local cykaPicture = image.load("System/OS/Icons/Steve.pic") --- local cykaPicture2 = image.load("System/OS/Icons/Love.pic") - -local function drawWindow(x, y) - - --Тени - local shadowTransparency = 0.6 - buffer.square(x + width, y + 1, 2, height, 0x000000, 0xFFFFFF, " ", shadowTransparency) - buffer.square(x + 2, y + height, width - 2, 1, 0x000000, 0xFFFFFF, " ", shadowTransparency) - - --Создаем прозрачную часть окна, где отображается "Избранное" - buffer.square(x, y, 20, height, 0xFFFFFF, 0xFFFFFF, " ", transparency) - - --Создаем непрозрачную часть окна для отображения всяких файлов и т.п. - buffer.square(x + 20, y, width - 20, height, 0xFFFFFF, 0xFFFFFF, " ") - - --Создаем три более темных полоски вверху окна, имитируя объем - buffer.square(x, y, width, 1, 0xDDDDDD, 0xFFFFFF, " ") - buffer.square(x, y + 1, width, 1, 0xCCCCCC, 0xFFFFFF, " ") - buffer.square(x, y + 2, width, 1, 0xBBBBBB, 0xFFFFFF, " ") - - --Рисуем заголовок нашего окошка - buffer.text(x + 30, y, 0x000000, "Тестовое окно") - - --Создаем три кнопки в левом верхнем углу для закрытия, разворачивания и сворачивания окна - buffer.set(x + 1, y, 0xDDDDDD, 0xFF4940, "⬤") - buffer.set(x + 3, y, 0xDDDDDD, 0xFFB640, "⬤") - buffer.set(x + 5, y, 0xDDDDDD, 0x00B640, "⬤") - - --Рисуем элементы "Избранного" чисто для демонстрации - buffer.text(x + 1, y + 3, 0x000000, "Избранное") - for i = 1, 6 do buffer.text(x + 2, y + 3 + i, 0x555555, "Вариант " .. i) end - - --Рисуем "Файлы" в виде желтых квадратиков. Нам без разницы, а выглядит красиво - for j = 1, 3 do - for i = 1, 5 do - local xPos, yPos = x + 22 + i*12 - 12, y + 4 + j*7 - 7 - buffer.square(xPos, yPos, 8, 4, 0xFFFF80, 0xFFFFFF, " ") - buffer.text(xPos, yPos + 5, 0x262626, "Файл " .. i .. "x" .. j) - end - end - - --Ну, и наконец рисуем голубой скроллбар справа - buffer.square(x + width - 1, y + 3, 1, height - 3, 0xBBBBBB, 0xFFFFFF, " ") - buffer.square(x + width - 1, y + 3, 1, 4, 0x3366CC, 0xFFFFFF, " ") - - --Изображения! - -- buffer.image(x + 23, y + 6, cykaPicture) - -- buffer.image(x + 33, y + 12, cykaPicture2) - - if showPanel then - xPos, yPos = x, y + height + 2 - buffer.square(xPos, yPos, width, 10, 0xFFFFFF, 0xFFFFFF, " ", transparency) - - --Тень - buffer.square(xPos + width, yPos + 1, 2, 10, 0x000000, 0xFFFFFF, " ", shadowTransparency) - buffer.square(xPos + 2, yPos + 10, width - 2, 1, 0x000000, 0xFFFFFF, " ", shadowTransparency) - - yPos = yPos + 1 - xPos = xPos + 2 - buffer.text(xPos + 2, yPos, 0x262626, "Клик левой кнопкой мыши: изменить позицию окошка"); yPos = yPos + 1 - buffer.text(xPos + 2, yPos, 0x262626, "Клик правой кнопкой: нарисовать еще одно такое же окошко"); yPos = yPos + 1 - buffer.text(xPos + 2, yPos, 0x262626, "Колесо мыши: изменить прозрачность окна"); yPos = yPos + 2 - buffer.text(xPos + 2, yPos, 0x262626, "Space: переключить фон между картинкой и статичным цветом"); yPos = yPos + 1 - buffer.text(xPos + 2, yPos, 0x262626, "Shift: изменить цвет фона на рандомный"); yPos = yPos + 1 - buffer.text(xPos + 2, yPos, 0x262626, "Tab: включить или отключить данную информационную панель"); yPos = yPos + 1 - buffer.text(xPos + 2, yPos, 0x262626, "Enter: выйти отсудова на хер"); yPos = yPos + 1 - end -end - -drawBackground() -drawWindow(xWindow, yWindow) -buffer.draw() - -while true do - local e = {event.pull()} - if e[1] == "touch" then - if e[5] == 0 then - drawBackground() - xWindow, yWindow = e[3], e[4] - drawWindow(xWindow, yWindow) - buffer.draw() - else - xWindow, yWindow = e[3], e[4] - drawWindow(xWindow, yWindow) - buffer.draw() - end - elseif e[1] == "key_down" then - if e[4] == 42 then - currentBackground = math.random(0x000000, 0xFFFFFF) - drawBackground() - drawWindow(xWindow, yWindow) - buffer.draw() - elseif e[4] == 28 then - buffer.square(1, 1, bufferWidth, bufferHeight, 0x262626, 0xFFFFFF, " ") - buffer.draw() - return - elseif e[4] == 57 then - risovatKartinku = not risovatKartinku - drawBackground() - drawWindow(xWindow, yWindow) - buffer.draw() - elseif e[4] == 15 then - showPanel = not showPanel - drawBackground() - drawWindow(xWindow, yWindow) - buffer.draw() - end - elseif e[1] == "scroll" then - if e[5] == 1 then - if transparency > 0.05 then - transparency = transparency - 0.05 - drawBackground() - drawWindow(xWindow, yWindow) - buffer.draw() - end - else - if transparency < 1 then - transparency = transparency + 0.05 - drawBackground() - drawWindow(xWindow, yWindow) - buffer.draw() - end - end - end -end - - - - - - diff --git a/Applications/BufferDemo/Icon.pic b/Applications/BufferDemo/Icon.pic deleted file mode 100644 index aa3c658a700e3d3d3879d6e027bb175c2f2fe3cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108 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 r6a6jI!O#Bz++aOvPcu(THD45b$gPhqW-6!;caA E07 countOfDays then break end - if i >= firstDay then startDrawing = true end - if startDrawing then gpu.set(xPos, yPos, tostring(counter)); counter = counter + 1 end - xPos = xPos + constants.xSpaceBetweenNumbers + 2 - end - yPos = yPos + constants.ySpaceBetweenNumbers + 1 - end -end - ---Получить номер следующего дня -local function getNextDay(day) - if day < 7 then - return (day + 1) - else - return 1 - end -end - ---Просчитать данные о годе -local function calculateYear(year, dayOf1Jan) - local massivGoda = {} - local visokosniy = visokosniy(year) - - local firstDayPosition = dayOf1Jan - - --Получаем количество дней в каждом месяце - for month = 1, 12 do - --Создаем подмассив месяца в массиве года - massivGoda[month] = {} - --Если это февраль - if month == 2 then - --Если год високосный - if visokosniy then - massivGoda[month].countOfDays = 29 - massivGoda[month].firstDayPosition = firstDayPosition - firstDayPosition = getNextDay(firstDayPosition) - --Если не високосный - else - massivGoda[month].countOfDays = 28 - massivGoda[month].firstDayPosition = firstDayPosition - end - --Если не февраль - else - massivGoda[month].countOfDays = countOfDays[month] - massivGoda[month].firstDayPosition = firstDayPosition - for i = 1, monthDateMove[month] do - firstDayPosition = getNextDay(firstDayPosition) - end - end - end - - return massivGoda -end - ---Получить день недели первого января указанного года -local function polu4itDenNedeliPervogoJanvarja(year, debug) - local den = 0 - - local difference = math.abs(year - 1010) - local koli4estvoVisokosnih - - if difference % 4 == 0 then - koli4estvoVisokosnih = difference / 4 - elseif difference % 4 == 1 then - koli4estvoVisokosnih = math.floor(difference / 4) - elseif difference % 4 == 2 then - koli4estvoVisokosnih = math.floor(difference / 4) - elseif difference % 4 == 3 then - koli4estvoVisokosnih = math.floor(difference / 4) + 1 - end - - local sdvig = difference + koli4estvoVisokosnih - - if sdvig % 7 == 0 then - den = 1 - else - den = sdvig % 7 + 1 - end - - if debug then - print("Год: "..year) - print("Разница в годах: "..difference) - print("Кол-во високосных: "..koli4estvoVisokosnih) - print("Сдвиг по дням: "..sdvig) - print("День недели: "..den) - print(" ") - end - - return den -end - ---Нарисовать календарь -local function drawCalendar(xPos, yPos, year) - ecs.square(xPos, yPos, 120, 48, constants.backgroundColor) - --Получаем позицию первого января указанного года - local janFirst = polu4itDenNedeliPervogoJanvarja(year) - --Получаем массив года - local massivGoda = calculateYear(year, janFirst) - - --Перебираем массив года - for i = 1, #massivGoda do - --Рисуем месяц - drawMonth(xPos, yPos, massivGoda[i].firstDayPosition, massivGoda[i].countOfDays, year, i) - --Корректируем коорды - xPos = xPos + constants.xSpaceBetweenMonths + 27 - if i % 4 == 0 then xPos = 3; yPos = yPos + constants.ySpaceBetweenMonths + 15 end - end -end - -local function drawSymbol(x, y, symbol) - local xPos, yPos = x, y - for j = 1, #numbers[symbol] do - xPos = x - for i = 1, #numbers[symbol][j] do - if numbers[symbol][j][i] ~= 0 then - gpu.set(xPos, yPos, " ") - end - xPos = xPos + 2 - end - yPos = yPos + 1 - end -end - -local function drawYear(x, y, year) - year = tostring(year) - for i = 1, #year do - drawSymbol(x, y, string.sub(year, i, i)) - x = x + 8 - end -end - -local next, prev - -local function drawInfo() - local xPos, yPos = 127, 4 - ecs.square(xPos, yPos, 30, 5, constants.backgroundColor) - gpu.setBackground(constants.bigNumberColor) - drawYear(xPos, yPos, constants.programYear) - yPos = yPos + 6 - - local name = "Следующий год"; newObj("Buttons", name, ecs.drawButton(xPos, yPos, 30, 3, name, 0xDDDDDD, 0x262626)); yPos = yPos + 4 - name = "Предыдущий год"; newObj("Buttons", name, ecs.drawButton(xPos, yPos, 30, 3, name, 0xDDDDDD, 0x262626)); yPos = yPos + 4 - name = "Выйти"; newObj("Buttons", name, ecs.drawButton(xPos, yPos, 30, 3, name, 0xDDDDDD, 0x262626)); yPos = yPos + 4 - -end - -local function drawAll() - --Очищаем экран - ecs.square(1, 1, xMax, yMax, constants.backgroundColor) - --Рисуем календарик - drawCalendar(3, 2, constants.programYear) - --Рисуем парашу - drawInfo() -end - --------------------------------------------------------------------------------------------------------------------- - -if xMax < 150 then error("This program requires Tier 3 GPU and Tier 3 Screen.") end ---Запоминаем старое разрешение экрана -local xOld, yOld = gpu.getResolution() ---Ставим максимальное -gpu.setResolution(xMax, yMax) ---Получаем данные о текущей дате (os.date выдает неверную дату и месяц, забавно) -constants.currentDay, constants.currentMonth, constants.currentYear = ecs.getHostTime(2) -constants.programDay, constants.programMonth, constants.programYear = constants.currentDay, constants.currentMonth, constants.currentYear ---Рисуем все -drawAll() - -while true do - local e = {event.pull()} - if e[1] == "touch" then - for key in pairs(obj["Buttons"]) do - if ecs.clickedAtArea(e[3], e[4], obj["Buttons"][key][1], obj["Buttons"][key][2], obj["Buttons"][key][3], obj["Buttons"][key][4]) then - ecs.drawButton(obj["Buttons"][key][1], obj["Buttons"][key][2], 30, 3, key, constants.weekendColor, constants.currentDayColor) - os.sleep(0.2) - - if key == "Следующий год" then - constants.programYear = constants.programYear + 1 - elseif key == "Предыдущий год" then - constants.programYear = constants.programYear - 1 - elseif key == "Выйти" then - gpu.setResolution(xOld, yOld) - ecs.prepareToExit() - return - end - - drawInfo() - drawCalendar(3, 2, constants.programYear) - - break - end - end - end -end - - - - - - - - diff --git a/Applications/Camera/Icon.pic b/Applications/Camera.app/Icon.pic similarity index 100% rename from Applications/Camera/Icon.pic rename to Applications/Camera.app/Icon.pic diff --git a/Applications/Camera/Main.lua b/Applications/Camera.app/Main.lua similarity index 82% rename from Applications/Camera/Main.lua rename to Applications/Camera.app/Main.lua index 0597761a..cf160d1b 100644 --- a/Applications/Camera/Main.lua +++ b/Applications/Camera.app/Main.lua @@ -1,18 +1,16 @@ -local computer = require("computer") -local component = require("component") local GUI = require("GUI") -local MineOSInterface = require("MineOSInterface") -local buffer = require("doubleBuffering") -local image = require("image") -local fs = require("filesystem") +local system = require("System") +local screen = require("Screen") +local image = require("Image") +local filesystem = require("Filesystem") if not component.isAvailable("camera") then GUI.alert("This program reqiures camera from computronix mod") return end -local cameraProxy = component.camera +local cameraProxy = component.get("camera") local grayscale = { 0xF0F0F0, @@ -58,7 +56,7 @@ local thermal = { } local palette = grayscale -local application, window = MineOSInterface.addWindow(GUI.filledWindow(1, 1, 100, 25, 0x2D2D2D)) +local workspace, window = system.addWindow(GUI.filledWindow(1, 1, 100, 25, 0x2D2D2D)) window.backgroundPanel.width = 22 window.backgroundPanel.height = window.height @@ -101,38 +99,38 @@ local function takePicture() x, y = 1, y + 1 end - MineOSInterface.application:draw() + workspace:draw() end -local buttonImage = image.load(fs.path(getCurrentScript()) .. "Icon.pic") +local buttonImage = image.load(filesystem.path(system.getCurrentScript()) .. "Icon.pic") local buttonImagePressed = image.blend(buttonImage, 0x0, 0.6) local shootButton = window:addChild(GUI.object(1, 1, 8, 4)) shootButton.draw = function() - buffer.drawImage(shootButton.x, shootButton.y, shootButton.pressed and buttonImagePressed or buttonImage) + screen.drawImage(shootButton.x, shootButton.y, shootButton.pressed and buttonImagePressed or buttonImage) end -shootButton.eventHandler = function(application, object, e1) +shootButton.eventHandler = function(workspace, object, e1) if e1 == "touch" then shootButton.pressed = true - MineOSInterface.application:draw() + workspace:draw() takePicture() shootButton.pressed = false - MineOSInterface.application:draw() + workspace:draw() end end cameraView.draw = function(cameraView) - buffer.drawRectangle(cameraView.x, cameraView.y, cameraView.width, cameraView.height, 0xF0F0F0, 0x878787, " ") + screen.drawRectangle(cameraView.x, cameraView.y, cameraView.width, cameraView.height, 0xF0F0F0, 0x878787, " ") local x, y = 0, 0 for y = 1, #cameraView.pixels do for x = 1, #cameraView.pixels[y] do local color = palette[math.ceil(cameraView.pixels[y][x] / rangeSlider.value * #palette)] or 0x0 if semiPixelSwitch.state then - buffer.semiPixelSet(cameraView.x + x - 1, cameraView.y * 2 + y - 2, color) + screen.semiPixelSet(cameraView.x + x - 1, cameraView.y * 2 + y - 2, color) else - buffer.set(cameraView.x + x - 1, cameraView.y + y - 2, color, 0x0, " ") + screen.set(cameraView.x + x - 1, cameraView.y + y - 2, color, 0x0, " ") end end end @@ -156,12 +154,12 @@ FOVSlider.onValueChanged = takePicture paletteSwitch.onStateChanged = function() palette = paletteSwitch.state and thermal or grayscale - MineOSInterface.application:draw() + workspace:draw() end autoupdateSwitch.onStateChanged = function() autoupdateSlider.hidden = not autoupdateSwitch.state - MineOSInterface.application:draw() + workspace:draw() end for address in component.list("camera") do @@ -180,7 +178,7 @@ window.onResize = function(width, height) shootButton.localX = math.floor(1 + window.backgroundPanel.width / 2 - shootButton.width / 2) shootButton.localY = window.height - shootButton.height - MineOSInterface.application:draw() + workspace:draw() takePicture() end diff --git a/Applications/Chat/About/English.txt b/Applications/Chat/About/English.txt deleted file mode 100644 index 8ae383a4..00000000 --- a/Applications/Chat/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -БЕТА, СУКА, НЕ ТРОГАЙ! РУКИ ПРОЧЬ!!! \ No newline at end of file diff --git a/Applications/Chat/About/Russian.txt b/Applications/Chat/About/Russian.txt deleted file mode 100644 index 8ae383a4..00000000 --- a/Applications/Chat/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -БЕТА, СУКА, НЕ ТРОГАЙ! РУКИ ПРОЧЬ!!! \ No newline at end of file diff --git a/Applications/Chat/Chat.lua b/Applications/Chat/Chat.lua deleted file mode 100644 index 7347733d..00000000 --- a/Applications/Chat/Chat.lua +++ /dev/null @@ -1,777 +0,0 @@ - -local event = require("event") -local modemConnection = require("modemConnection") -local ecs = require("ECSAPI") -local fs = require("filesystem") -local serialization = require("serialization") -local buffer = require("doubleBuffering") -local context = require("context") -local image = require("image") -local unicode = require("unicode") -local component = require("component") -local computer = require("computer") - -------------------------------------------------------------------------------------------------------------------------------- - -if not component.isAvailable("modem") then ecs.error("This program requires wireless modem to work!"); return end - -local colors = { - leftBar = 0x262626, - leftBarSelection = 0x00A8FF, - leftBarAlternative = 0x383838, - leftBarText = 0xFFFFFF, - leftBarSelectionText = 0xFFFFFF, - leftBarSearchButton = 0x555555, - leftBarSearchButtonText = 0xFFFFFF, - - scrollBar = 0xCCCCCC, - scrollBarPipe = 0x666666, - - topBar = 0xEEEEEE, - topBarName = 0x000000, - topBarAddress = 0x555555, - - topMenu = 0xFFFFFF, - - chatZone = 0xFFFFFF, - senderCloudColor = 0x3392FF, - senderCloudTextColor = 0xFFFFFF, - yourCloudColor = 0x55BBFF, - yourCloudTextColor = 0xFFFFFF, - systemMessageColor = 0x555555, - - sendButtonColor = 0x555555, - sendButtonTextColor = 0xFFFFFF, - - messageInputBarColor = 0xEEEEEE, - messageInputBarInputBackgroundColor = 0xFFFFFF, - messsageInputBarButtonColor = 0x3392FF, - messsageInputBarButtonTextColor = 0xFFFFFF, - messsageInputBarTextColor = 0x262626, -} - -local chatHistory = {} -local avatars = {} -local port = 899 -local modem = component.modem -modem.open(port) - -------------------------------------------------------------------------------------------------------------------------------- - -local pathToApplication = "/MineOS/Applications/Chat.app/" -local pathToSaveSendedFiles = "/MineOS/Downloads/" -local contactsAvatarsPath = pathToApplication .. "Resources/Avatars/" -local personalAvatarPath = contactsAvatarsPath .. "MyAvatar.pic" -local chatHistoryPath = "MineOS/System/Chat/History.cfg" -local avatarWidthLimit = 6 -local avatarHeightLimit = 3 - -local currentChatID = 1 -local currentChatMessage = 1 -local currentMessageText - -buffer.start() -local messageInputHeight = 5 -local leftBarHeight = buffer.screen.height - 9 -local leftBarWidth = math.floor(buffer.screen.width * 0.2) -local chatZoneWidth = buffer.screen.width - leftBarWidth -local heightOfTopBar = 2 + avatarHeightLimit -local yLeftBar = 2 + heightOfTopBar -local chatZoneX = leftBarWidth + 1 -local bottom -local chatZoneHeight = buffer.screen.height - yLeftBar - messageInputHeight + 1 -local cloudWidth = chatZoneWidth - 2 * (avatarWidthLimit + 9) -local cloudTextWidth = cloudWidth - 4 -local yMessageInput = buffer.screen.height - messageInputHeight + 1 -local sendButtonWidth = 7 -local messageInputWidth = chatZoneWidth - sendButtonWidth - 6 - -------------------------------------------------------------------------------------------------------------------------------- - ---Объекты для тача -local obj = {} -local function newObj(class, name, ...) - obj[class] = obj[class] or {} - obj[class][name] = {...} -end - -local function saveChatHistory() - fs.makeDirectory(fs.path(chatHistoryPath) or "") - local file = io.open(chatHistoryPath, "w") - file:write(serialization.serialize(chatHistory)) - file:close() -end - -local function loadChatHistory() - if fs.exists(chatHistoryPath) then - local file = io.open(chatHistoryPath, "r") - chatHistory = serialization.unserialize(file:read("*a")) - file:close() - else - chatHistory = {myName = "Аноним №" .. math.random(1, 1000)} - saveChatHistory() - end -end - -local function loadAvatarFromFile(path) - local avatar = image.load(path) - local widthDifference = avatar.width - avatarWidthLimit - local heightDifference = avatar.height - avatarHeightLimit - - if widthDifference > 0 then - avatar = image.crop(avatar, "fromRight", widthDifference) - end - if heightDifference > 0 then - avatar = image.crop(avatar, "fromBottom", heightDifference) - end - - return avatar -end - -local function loadPersonalAvatar() - avatars.personal = loadAvatarFromFile(personalAvatarPath) -end - -local function loadContactAvatar(ID) - avatars.contact = loadAvatarFromFile(contactsAvatarsPath .. ID .. ".pic") -end - -local function saveContactAvatar(ID, data) - local file = io.open(contactsAvatarsPath .. ID .. ".pic", "w") - file:write(data) - file:close() -end - -local function switchToContact(ID) - currentChatID = ID - currentChatMessage = #chatHistory[currentChatID] - loadContactAvatar(currentChatID) - chatHistory[currentChatID].unreadedMessages = nil -end - -local function drawLeftBar() - buffer.square(1, yLeftBar, leftBarWidth, leftBarHeight, colors.leftBar, 0xFFFFFF, " ") - - local howMuchContactsCanBeShown = math.floor(leftBarHeight / 3) - obj.Contacts = {} - - local yPos = yLeftBar - local counter = 1 - local text, textColor - - for i = 1, #chatHistory do - textColor = colors.leftBarText - - --Рисуем подложку - if i == currentChatID then - buffer.square(1, yPos, leftBarWidth, 3, colors.leftBarSelection, 0xFFFFFF, " ") - textColor = 0xFFFFFF - elseif counter % 2 ~= 0 then - buffer.square(1, yPos, leftBarWidth, 3, colors.leftBarAlternative, 0xFFFFFF, " ") - end - - --Создаем объекты для клика - newObj("Contacts", i, 1, yPos, leftBarWidth, yPos + 2) - - --Рендерим корректное имя - text = chatHistory[i].name or address - text = ecs.stringLimit("end", text, leftBarWidth - 4) - - --Рисуем имя - yPos = yPos + 1 - buffer.text(2, yPos, textColor, text) - - --Если имеются непрочитанные сообщения, то показать их - if chatHistory[i].unreadedMessages then - local stringCount = tostring(chatHistory[i].unreadedMessages) - local stringCountLength = unicode.len(stringCount) - local x = leftBarWidth - 3 - stringCountLength - buffer.square(x, yPos, stringCountLength + 2, 1, colors.leftBarText, 0xFFFFFF, " ") - buffer.text(x + 1, yPos, colors.leftBar, stringCount) - end - - yPos = yPos + 2 - counter = counter + 1 - if counter > howMuchContactsCanBeShown or yPos > buffer.screen.height then - break - end - end - - --Кнопочка поиска юзеров - obj.search = {buffer.button(1, buffer.screen.height - 2, leftBarWidth, 3, colors.leftBarSearchButton, colors.leftBarSearchButtonText, "Поиск")} -end - -local function drawTopBar() - buffer.square(1, 2, buffer.screen.width, heightOfTopBar, colors.topBar, 0xFFFFFF, " ") - - buffer.image(3, 3, avatars.personal) - buffer.text(11, 3, colors.topBarName, chatHistory.myName) - buffer.text(11, 4, colors.topBarAddress, modemConnection.localAddress) -end - -local function drawTopMenu() - obj.TopMenu = buffer.menu(1, 1, buffer.screen.width, colors.topMenu, 0, {"Чат", 0x000099}, {"История", 0x000000}, {"О программе", 0x000000}) -end - -local function drawEmptyCloud(x, y, cloudWidth, cloudHeight, cloudColor, fromYou) - local upperPixel = "▀" - local lowerPixel = "▄" - - --Рисуем финтифлюшечки - if not fromYou then - buffer.set(x, y - cloudHeight + 2, colors.chatZone, cloudColor, upperPixel) - buffer.set(x + 1, y - cloudHeight + 2, cloudColor, 0xFFFFFF, " ") - x = x + 2 - else - buffer.set(x + cloudWidth + 3, y - cloudHeight + 2, colors.chatZone, cloudColor, upperPixel) - buffer.set(x + cloudWidth + 2, y - cloudHeight + 2, cloudColor, 0xFFFFFF, " ") - end - - --Заполняшечки - buffer.square(x + 1, y - cloudHeight + 1, cloudWidth, cloudHeight, cloudColor, 0xFFFFFF, " ") - buffer.square(x, y - cloudHeight + 2, cloudWidth + 2, cloudHeight - 2, cloudColor, 0xFFFFFF, " ") - - --Сгругленные краешки - buffer.set(x, y - cloudHeight + 1, colors.chatZone, cloudColor, lowerPixel) - buffer.set(x + cloudWidth + 1, y - cloudHeight + 1, colors.chatZone, cloudColor, lowerPixel) - buffer.set(x, y, colors.chatZone, cloudColor, upperPixel) - buffer.set(x + cloudWidth + 1, y, colors.chatZone, cloudColor, upperPixel) - - return y - cloudHeight + 1 -end - -local function drawTextCloud(x, y, cloudColor, textColor, fromYou, text) - local y = drawEmptyCloud(x, y, cloudTextWidth, #text + 2, cloudColor, fromYou) - x = fromYou and x + 2 or x + 4 - - for i = 1, #text do - buffer.text(x, y + i, textColor, text[i]) - end - - return y -end - -local function drawFileCloud(x, y, cloudColor, textColor, fromYou, fileName) - local y = drawEmptyCloud(x, y, 14, 8, cloudColor, fromYou) - x = fromYou and x + 2 or x + 4 - - ecs.drawOSIcon(x, y + 1, fileName, true, textColor) - - return y -end - -local function stringWrap(text, limit) - local strings = {} - local textLength = unicode.len(text) - local subFrom = 1 - while subFrom <= textLength do - table.insert(strings, unicode.sub(text, subFrom, subFrom + limit - 1)) - subFrom = subFrom + limit - end - return strings -end - -local function drawChat() - - local x, y = chatZoneX, yLeftBar - buffer.square(x, y, chatZoneWidth, chatZoneHeight, colors.chatZone, 0xFFFFFF, " ") - - --Если отстутствуют контакты, то отобразить стартовое сообщение - if not chatHistory[currentChatID] then - local text = ecs.stringLimit("start", "Добавьте контакты с помощью кнопки \"Поиск\"", chatZoneWidth - 2) - local x, y = math.floor(chatZoneX + chatZoneWidth / 2 - unicode.len(text) / 2), math.floor(yLeftBar + chatZoneHeight / 2) - buffer.text(x, y, 0x555555, text) - return - end - - -- Ставим ограничение отрисовки буфера, чтобы облачка сообщений не ебошили - -- За края верхней зоны чатзоны, ну ты понял, да? - buffer.setDrawLimit(x, y, chatZoneWidth, chatZoneHeight) - - -- Стартовая точка - y = buffer.screen.height - messageInputHeight - 1 - local xYou, xSender = x + 2, buffer.screen.width - 8 - -- Отрисовка облачков - for i = currentChatMessage, 1, -1 do - --Если не указан тип сообщения, то ренедрим дефолтные облачка - if not chatHistory[currentChatID][i].type then - --Если сообщенька от тебя, то цвет меняем - if chatHistory[currentChatID][i].fromYou then - y = drawTextCloud(xSender - cloudWidth - 2, y, colors.yourCloudColor, colors.yourCloudTextColor, chatHistory[currentChatID][i].fromYou, stringWrap(chatHistory[currentChatID][i].message, cloudTextWidth - 2)) - buffer.image(xSender, y, avatars.personal) - else - y = drawTextCloud(xYou + 8, y, colors.senderCloudColor, colors.senderCloudTextColor, chatHistory[currentChatID][i].fromYou, stringWrap(chatHistory[currentChatID][i].message, cloudTextWidth)) - buffer.image(xYou, y, avatars.contact) - end - --Если сообщение имеет тип "Файл" - elseif chatHistory[currentChatID][i].type == "file" then - if chatHistory[currentChatID][i].fromYou then - y = drawFileCloud(xSender - 20, y, colors.yourCloudColor, colors.yourCloudTextColor, chatHistory[currentChatID][i].fromYou, chatHistory[currentChatID][i].message) - buffer.image(xSender, y, avatars.personal) - else - y = drawFileCloud(xYou + 8, y, colors.senderCloudColor, colors.senderCloudTextColor, chatHistory[currentChatID][i].fromYou, chatHistory[currentChatID][i].message) - buffer.image(xYou, y, avatars.contact) - end - else - for i = chatZoneX, buffer.screen.width - 2 do - buffer.set(i, y, colors.chatZone, colors.systemMessageColor, "─") - end - local x = math.floor(chatZoneX + (chatZoneWidth - 2) / 2 - unicode.len(chatHistory[currentChatID][i].message) / 2) - buffer.text(x, y, colors.systemMessageColor, " " .. chatHistory[currentChatID][i].message .. " ") - end - - y = y - 2 - if y <= yLeftBar then break end - end - - -- Убираем ограничение отроисовки буфера - buffer.resetDrawLimit() - - buffer.scrollBar(buffer.screen.width, yLeftBar, 1, chatZoneHeight, #chatHistory[currentChatID], currentChatMessage, colors.scrollBar, colors.scrollBarPipe) -end - -local function drawMessageInputBar() - local x, y = chatZoneX, yMessageInput - buffer.square(x, y, chatZoneWidth, messageInputHeight, colors.messageInputBarColor, 0xFFFFFF, " ") - y = y + 1 - buffer.square(x + 2, y, messageInputWidth, 3, colors.messageInputBarInputBackgroundColor, 0xFFFFFF, " ") - buffer.text(x + 3, y + 1, colors.messsageInputBarTextColor, ecs.stringLimit("start", currentMessageText or "Введите сообщение", messageInputWidth - 2)) - - obj.send = {buffer.button(chatZoneX + messageInputWidth + 4, y, sendButtonWidth, 3, colors.sendButtonColor, colors.sendButtonTextColor, "⇪")} -end - -local function drawAll(force) - drawTopBar() - drawLeftBar() - drawTopMenu() - drawChat() - drawMessageInputBar() - buffer.draw(force) -end - -local function scrollChat(direction) - if direction == 1 then - if currentChatMessage > 1 then - currentChatMessage = currentChatMessage - 1 - drawChat() - drawMessageInputBar() - buffer.draw() - end - else - if currentChatMessage < #chatHistory[currentChatID] then - currentChatMessage = currentChatMessage + 1 - drawChat() - drawMessageInputBar() - buffer.draw() - end - end -end - -local function addMessageToArray(ID, type, fromYou, message) - table.insert(chatHistory[ID], {type = type, fromYou = fromYou, message = message}) - saveChatHistory() -end - -local function sendMessage(type, message) - modem.send(chatHistory[currentChatID].address, port, "HereIsMessageToYou", type, message) - - addMessageToArray(currentChatID, nil, true, currentMessageText) - - currentChatMessage = #chatHistory[currentChatID] - currentMessageText = nil -end - -local function checkAddressExists(address) - for i = 1, #chatHistory do - if chatHistory[i].address == address then - return true - end - end - return false -end - -local function addNewContact(address, name, avatarData) - if not checkAddressExists(address) then - table.insert(chatHistory, - { - address = address, - name = name, - { - type = "system", - message = "Здесь будет показана история чата" - } - }) - saveChatHistory() - saveContactAvatar(#chatHistory, avatarData) - end -end - -local function askForAddToContacts(address) - --Загружаем авку - local file = io.open(personalAvatarPath, "r") - local avatarData = file:read("*a") - file:close() - --Отсылаем свое имечко и аватарку - modem.send(address, port, "AddMeToContactsPlease", chatHistory.myName, avatarData) -end - -local function getNameAndIDOfAddress(address) - for i = 1, #chatHistory do - if chatHistory[i].address == address then - return chatHistory[i].name, i - end - end - return nil, nil -end - -local function autoScroll() - --Если мы никуда не скроллили и находимся в конце истории чата с этим юзером - --То автоматически проскроллить на конец - if currentChatMessage == (#chatHistory[currentChatID] - 1) then - currentChatMessage = #chatHistory[currentChatID] - end -end - -local function receiveFile(remoteAddress, fileName) - --Чекаем, есть ли он в контактах - if checkAddressExists(remoteAddress) then - --Создаем директорию под файлики, а то мало ли - fs.makeDirectory(pathToSaveSendedFiles) - --Получаем имя отправителя из контактов - local senderName, senderID = getNameAndIDOfAddress(remoteAddress) - --Открываем файл для записи - local file = io.open(pathToSaveSendedFiles .. fileName, "w") - --Запоминаем пиксели под окошком прогресса - local oldPixels = ecs.progressWindow("auto", "auto", 40, 0, "Прием файла", true) - --Начинаем ожидать беспроводных сообщений в течение 10 секунд - while true do - local fileReceiveEvent = { event.pull(10, "modem_message") } - --Это сообщение несет в себе процентаж передачи и сами данные пакета - if fileReceiveEvent[6] == "FILESEND" then - --Рисуем окошко прогресса - ecs.progressWindow("auto", "auto", 40, fileReceiveEvent[7], "Прием файла") - file:write(fileReceiveEvent[8]) - --Если нам присылают сообщение о завершении передачи, то закрываем файл - elseif fileReceiveEvent[6] == "FILESENDEND" then - ecs.progressWindow("auto", "auto", 40, 100, "Прием файла") - file:close() - ecs.drawOldPixels(oldPixels) - - --Вставляем сообщение с файликом-иконочкой - addMessageToArray(senderID, "file", nil, fileName) - autoScroll() - drawAll() - - --Выдаем окошечко о том, что файл успешно передан - ecs.universalWindow("auto", "auto", 30, 0x262626, true, - {"EmptyLine"}, - {"CenterText", ecs.colors.orange, "Прием данных завершен"}, - {"EmptyLine"}, - {"CenterText", 0xFFFFFF, "Файл от " .. senderName .. " сохранен как"}, - {"CenterText", 0xFFFFFF, "\"" .. pathToSaveSendedFiles .. fileName .. "\""}, - {"EmptyLine"}, - {"Button", {ecs.colors.orange, 0x262626, "OK"}} - ) - - break - --Если не получали в течение указанного промежутка сообщений, то выдать сообщение об ошибке и удалить файл - elseif not fileReceiveEvent[1] then - file:close() - ecs.drawOldPixels(oldPixels) - fs.remove(pathToSaveSendedFiles .. fileName) - ecs.error("Ошибка при передаче файла: клиент не отвечает") - drawAll() - break - end - end - end -end - ---Обработчик сообщений -local function dro4er(_, localAddress, remoteAddress, remotePort, distance, ...) - local messages = { ... } - - if remotePort == port then - if messages[1] == "AddMeToContactsPlease" then - if modemConnection.remoteAddress and modemConnection.remoteAddress == remoteAddress then - -- ecs.error("Сообщение о добавлении получил, адрес: " .. modemConnection.remoteAddress .. ", имя:" .. messages[2] .. ", авка: " .. type(messages[3])) - --Добавляем пидорка к себе в контакты - addNewContact(modemConnection.remoteAddress, messages[2], messages[3]) - --Просим того пидорка, чтобы он добавил нас к себе в контакты - askForAddToContacts(modemConnection.remoteAddress) - --Чтобы не было всяких соблазнов! - modemConnection.availableUsers = {} - modemConnection.remoteAddress = nil - --Переключаемся на добавленный контакт - switchToContact(#chatHistory) - drawAll() - end - --Если какой-то чувак просит нас принять файл - elseif messages[1] == "FAYLPRIMI" then - receiveFile(remoteAddress, messages[2]) - elseif messages[1] == "HereIsMessageToYou" then - for i = 1, #chatHistory do - --Если в массиве истории чата найден юзер, отославший такое сообщение - if chatHistory[i].address == remoteAddress then - --То вставляем само сообщение в историю чата - addMessageToArray(i, messages[2], nil, messages[3]) - --Если текущая открытая история чата является именно вот этой, с этим отправителем - if currentChatID == i then - autoScroll() - --Обязательно отрисовываем измененную историю чата с этим отправителем - drawChat() - buffer.draw() - --Увеличиваем количество непрочитанных сообщений от отправителя - else - chatHistory[i].unreadedMessages = chatHistory[i].unreadedMessages and chatHistory[i].unreadedMessages + 1 or 1 - drawLeftBar() - buffer.draw() - end - - --Бип! - computer.beep(1700) - - --А это небольшой костыльчик, чтобы не сбивался цвет курсора Term API - component.gpu.setBackground(colors.messageInputBarInputBackgroundColor) - component.gpu.setForeground(colors.messsageInputBarTextColor) - - break - end - end - end - end -end - -local function enableDro4er() - event.listen("modem_message", dro4er) -end - -local function disableDro4er() - event.ignore("modem_message", dro4er) -end - -local function deleteAvatar(ID) - fs.remove(contactsAvatarsPath .. ID .. ".pic") -end - -local function clearChatHistory() - for i = 1, #chatHistory do - deleteAvatar(i) - chatHistory[i] = nil - end - saveChatHistory() -end - -local function sendFile(path) - local data = ecs.universalWindow("auto", "auto", 30, 0x262626, true, - {"EmptyLine"}, - {"CenterText", ecs.colors.orange, "Отправить файл"}, - {"EmptyLine"}, - {"Input", 0xFFFFFF, ecs.colors.orange, "Путь"}, - {"EmptyLine"}, - {"Button", {ecs.colors.orange, 0x262626, "OK"}, {0x666666, 0xffffff, "Отмена"}} - ) - - if data[2] == "OK" then - if fs.exists(data[1]) then - --Отправляем сообщение о том, что мы собираемся отправить файл - modem.send(chatHistory[currentChatID].address, port, "FAYLPRIMI", fs.name(data[1])) - --Открываем файл и отправляем его по количеству пакетов - local maxPacketSize = modem.maxPacketSize() - 32 - local file = io.open(data[1], "rb") - local fileSize = fs.size(data[1]) - local percent = 0 - local sendedBytes = 0 - local dataToSend - - while true do - dataToSend = file:read(maxPacketSize) - if dataToSend then - modem.send(chatHistory[currentChatID].address, port, "FILESEND", percent, dataToSend) - sendedBytes = sendedBytes + maxPacketSize - percent = math.floor(sendedBytes / fileSize * 100) - else - break - end - end - - file:close() - --Репортуем об окончании передачи файла - modem.send(chatHistory[currentChatID].address, port, "FILESENDEND") - --Вставляем в чат инфу об обтправленном файле - addMessageToArray(currentChatID, "file", true, fs.name(data[1])) - autoScroll() - drawAll() - else - ecs.error("Файл \"" .. data[1] .. "\" не существует.") - end - end -end - -local function deleteContact(ID) - table.remove(chatHistory, ID) - deleteAvatar(ID) - if #chatHistory > 0 then - switchToContact(1) - else - currentChatID = 1 - currentChatMessage = 1 - end - saveChatHistory() -end - -------------------------------------------------------------------------------------------------------------------------------- - ---Отключаем принудительное завершение программы -ecs.disableInterrupting() ---Загружаем историю чата и свою аватарку -loadChatHistory() -loadPersonalAvatar() ---Если история не пуста, то переключаемся на указанный контакт -if chatHistory[currentChatID] then - switchToContact(currentChatID) -end ---Включаем прием данных по модему для подключения -modemConnection.startReceivingData() ---Отсылаем всем модемам сигнал о том, чтобы они удалили нас из своего списка -modemConnection.disconnect() ---Отправляем свои данные, чтобы нас заново внесли в список -modemConnection.sendPersonalData() ---Активируем прием сообщений чата -enableDro4er() ---Рисуем весь интерфейс -drawAll() - -------------------------------------------------------------------------------------------------------------------------------- - -while true do - local e = { event.pull() } - if e[1] == "touch" then - -- Клик на поле ввода сообщения - if #chatHistory > 0 and ecs.clickedAtArea(e[3], e[4], chatZoneX + 2, yMessageInput, chatZoneX + messageInputWidth + 2, yMessageInput + 3) then - local text = ecs.inputText(chatZoneX + 3, yMessageInput + 2, messageInputWidth - 2, currentMessageText, colors.messageInputBarInputBackgroundColor, colors.messsageInputBarTextColor) - if text ~= nil and text ~= "" then - currentMessageText = text - sendMessage(nil, currentMessageText) - buffer.square(chatZoneX + 2, yMessageInput + 1, messageInputWidth, 3, colors.messageInputBarInputBackgroundColor, 0xFFFFFF, " ") - buffer.draw() - drawMessageInputBar() - drawChat() - buffer.draw() - end - -- Жмякаем на кнопочку "Отправить" - elseif #chatHistory > 0 and ecs.clickedAtArea(e[3], e[4], obj.send[1], obj.send[2], obj.send[3], obj.send[4]) then - buffer.button(obj.send[1], obj.send[2], sendButtonWidth, 3, colors.sendButtonTextColor, colors.sendButtonColor, "⇪") - buffer.draw() - os.sleep(0.2) - drawMessageInputBar() - buffer.draw() - sendFile() - -- Кнопа поиска - elseif ecs.clickedAtArea(e[3], e[4], obj.search[1], obj.search[2], obj.search[3], obj.search[4]) then - buffer.button(obj.search[1], obj.search[2], leftBarWidth, 3, colors.leftBarSearchButtonText, colors.leftBarSearchButton, "Поиск") - buffer.draw() - os.sleep(0.2) - - modemConnection.search() - - --Если после поиска мы подключились к какому-либо адресу - if modemConnection.remoteAddress then - --Просим адрес добавить нас в свой список контактов - askForAddToContacts(modemConnection.remoteAddress) - end - - drawAll(true) - end - - --Клик на контакты - for key in pairs(obj.Contacts) do - if ecs.clickedAtArea(e[3], e[4], obj.Contacts[key][1], obj.Contacts[key][2], obj.Contacts[key][3], obj.Contacts[key][4]) then - if e[5] == 0 then - switchToContact(key) - drawAll() - else - local action = context.menu(e[3], e[4], {"Переименовать"}, {"Удалить"}) - if action == "Переименовать" then - local data = ecs.universalWindow("auto", "auto", 30, 0x262626, true, - {"EmptyLine"}, - {"CenterText", ecs.colors.orange, "Переименовать контакт"}, - {"EmptyLine"}, - {"Input", 0xFFFFFF, ecs.colors.orange, chatHistory[key].name}, - {"EmptyLine"}, - {"Button", {ecs.colors.orange, 0x262626, "OK"}, {0x666666, 0xffffff, "Отмена"}} - ) - - if data[2] == "OK" then - chatHistory[key].name = data[1] or chatHistory[key].name - drawAll() - end - elseif action == "Удалить" then - deleteContact(key) - drawAll() - end - end - - break - end - end - - for key in pairs(obj.TopMenu) do - if ecs.clickedAtArea(e[3], e[4], obj.TopMenu[key][1], obj.TopMenu[key][2], obj.TopMenu[key][3],obj.TopMenu[key][4]) then - buffer.button(obj.TopMenu[key][1] - 1, obj.TopMenu[key][2], unicode.len(key) + 2, 1, ecs.colors.blue, 0xFFFFFF, key) - buffer.draw() - - local action - if key == "Чат" then - action = context.menu(obj.TopMenu[key][1] - 1, obj.TopMenu[key][2] + 1, {"Изменить имя"}, {"Изменить аватар"}, {"Очистить историю"},"-", {"Выход"}) - elseif key == "О программе" then - ecs.universalWindow("auto", "auto", 36, 0x262626, true, - {"EmptyLine"}, - {"CenterText", ecs.colors.orange, "Chat v1.0"}, - {"EmptyLine"}, - {"CenterText", 0xFFFFFF, "Автор:"}, - {"CenterText", 0xBBBBBB, "Тимофеев Игорь"}, - {"CenterText", 0xBBBBBB, "vk.com/id7799889"}, - {"EmptyLine"}, - {"CenterText", 0xFFFFFF, "Тестеры:"}, - {"CenterText", 0xBBBBBB, "Егор Палиев"}, - {"CenterText", 0xBBBBBB, "vk.com/mrherobrine"}, - {"CenterText", 0xBBBBBB, "Максим Хлебников"}, - {"CenterText", 0xBBBBBB, "vk.com/mskalash"}, - {"EmptyLine"}, - {"Button", {ecs.colors.orange, 0x262626, "OK"}} - ) - end - - if action == "Выход" then - disableDro4er() - modemConnection.stopReceivingData() - modemConnection.disconnect() - ecs.enableInterrupting() - modem.close(port) - buffer.clear() - ecs.prepareToExit() - return - elseif action == "Очистить историю" then - clearChatHistory() - drawAll() - end - - drawTopMenu() - buffer.draw() - - break - end - end - - elseif e[1] == "scroll" then - if #chatHistory > 0 and ecs.clickedAtArea(e[3], e[4], chatZoneX, yLeftBar, chatZoneX + chatZoneWidth - 1, yLeftBar + chatZoneHeight - 1) then - scrollChat(e[5]) - end - end -end - - - - - diff --git a/Applications/Chat/Icon.pic b/Applications/Chat/Icon.pic deleted file mode 100644 index 281a630864fc0f8061075388d40b8da2f46a933b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 91 zcmW-Zu?>JQ5Ch-#ll%yjOu!P6kf>5H3M()JqcH%BgoMJUYtFUoTMCDn7CtZ_K>3t% hG?*ymkmTXRRvauK!A5m@-+NzZocUKi(i2iY_yDLA3043A diff --git a/Applications/Chat/MyAvatar.pic b/Applications/Chat/MyAvatar.pic deleted file mode 100644 index 4550254d742d128bd650d215d78de19020d423de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 87 zcmeZw_H<)oV`gMvU{_###K^$J$i~RT%)-jX45br+qW>A07@2~h48|NNgEbe*U`~ZH J*b1Nw764G@5Ly5L diff --git a/Applications/Christmas Tree.app/Icon.pic b/Applications/Christmas Tree.app/Icon.pic new file mode 100755 index 0000000000000000000000000000000000000000..21b99f77dde238766937d379302d3bd60a09d202 GIT binary patch literal 160 zcmXZVu?@mN3 deadline then + -- генерируем снежинку + local x, y, z, pixel = math.random(1, 46), 32, math.random(1, 46) + table.insert(tSnow, {x = x, y = y, z = z}) + hologram.set(x, y, z, 1) + + -- сдвигаем снежинки вниз + local i = 1 + while i <= #tSnow do + if tSnow[i].y > 1 then + x, y, z = tSnow[i].x + math.random(-1, 1), tSnow[i].y - 1, tSnow[i].z + math.random(-1, 1) + + if x < 1 then + x = 1 + elseif x > 46 then + x = 46 + end + + if z < 1 then + z = 1 + elseif z > 46 then + z = 46 + end + + pixel = hologram.get(x, y, z) + + if pixel == 0 or pixel == 1 then + hologram.set(tSnow[i].x, tSnow[i].y, tSnow[i].z, 0) + hologram.set(x, y, z, 1) + + tSnow[i].x, tSnow[i].y, tSnow[i].z = x, y, z + i = i + 1 + else + table.remove(tSnow,i) + end + else + table.remove(tSnow,i) + end + end + + deadline = computer.uptime() + 1 - speedSlider.value + end +end + +----------------------------------------------------------- + +-- Сначала интерфейс +workspace:draw() + +-- очищаем прожектор +hologram.clear() +scaleSlider.onValueChanged() +rotationSlider.onValueChanged() + +-- создаем палитру цветов +hologram.setPaletteColor(1, 0xFFFFFF) -- снег +hologram.setPaletteColor(2, 0x221100) -- ствол +hologram.setPaletteColor(3, 0x005522) -- хвоя + + -- задействуем алгоритм Брезенхэма для рисования кругов +local function cricle(x0, y, z0, R, i) + local x = R + local z = 0 + local err = -R + while z <= x do + hologram.set(x + x0, y, z + z0, i) + hologram.set(z + x0, y, x + z0, i) + hologram.set(-x + x0, y, z + z0, i) + hologram.set(-z + x0, y, x + z0, i) + hologram.set(-x + x0, y, -z + z0, i) + hologram.set(-z + x0, y, -x + z0, i) + hologram.set(x + x0, y, -z + z0, i) + hologram.set(z + x0, y, -x + z0, i) + z = z + 1 + if err <= 0 then + err = err + (2 * z + 1) + else + x = x - 1 + err = err + (2 * (z - x) + 1) + end + end +end + + -- отрисовываем основание ствола +for i = 1, 5 do + cricle(23, i, 23, tSpruce[i], 2) + cricle(23, i, 23, tSpruce[i]-1, 2) +end + +-- отрисовываем хвою +for j = 5, #tSpruce do + cricle(23, j, 23, tSpruce[j]-1, 3) + cricle(23, j, 23, tSpruce[j]-2, 3) +end diff --git a/Applications/ChristmasTree/About/English.txt b/Applications/ChristmasTree/About/English.txt deleted file mode 100644 index b0f5477c..00000000 --- a/Applications/ChristmasTree/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Красивая новогодняя программа, написанная разработчиком Doob, сотворяющая атмосферу праздника в любом месте, где бы вы ни находились. \ No newline at end of file diff --git a/Applications/ChristmasTree/About/Russian.txt b/Applications/ChristmasTree/About/Russian.txt deleted file mode 100644 index b0f5477c..00000000 --- a/Applications/ChristmasTree/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Красивая новогодняя программа, написанная разработчиком Doob, сотворяющая атмосферу праздника в любом месте, где бы вы ни находились. \ No newline at end of file diff --git a/Applications/ChristmasTree/ChristmasTree.lua b/Applications/ChristmasTree/ChristmasTree.lua deleted file mode 100644 index d676ec51..00000000 --- a/Applications/ChristmasTree/ChristmasTree.lua +++ /dev/null @@ -1,148 +0,0 @@ -local component = require("component") -local ecs = require("ECSAPI") -local hologram -local c = 23 - ------------------------------------------------------------ - -if not component.isAvailable("hologram") then - ecs.error("Этой программе необходим голографический проектор 2 уровня.") - return -else - hologram = component.hologram -end - ------------------------------------------------------------ - -local data = ecs.universalWindow("auto", "auto", 36, 0x262626, true, - {"EmptyLine"}, - {"CenterText", ecs.colors.orange, "Настройки анимации"}, - {"EmptyLine"}, - {"Selector", 0xFFFFFF, ecs.colors.orange, "Снегопад", "Легкий снежок", "Что-то сыпется", "Без осадков"}, - {"Selector", 0xFFFFFF, ecs.colors.orange, "Легкое вращение", "Быстрое вращение", "Турбина", "Дюраселл", "Без вращения"}, - {"EmptyLine"}, - {"CenterText", ecs.colors.orange, "Размер елочки"}, - {"EmptyLine"}, - {"Slider", ecs.colors.white, ecs.colors.orange, 1, 100, 50, "", ""}, - {"EmptyLine"}, - {"CenterText", ecs.colors.white, "Программа закрывается через"}, - {"CenterText", ecs.colors.white, "CTRL + ALT + C"}, - {"EmptyLine"}, - {"Button", {ecs.colors.orange, 0xffffff, "OK"}, {0x999999, 0xffffff, "Отмена"}} -) - -local snowMode, rotationMode, scale, buttonPress = data[1], data[2], data[3], data[4] - -scale = scale * 4 / 100 - -if snowMode == "Снегопад" then - snowMode = 0 -elseif snowMode == "Легкий снежок" then - snowMode = 0.2 -elseif snowMode == "Что-то сыпется" then - snowMode = 0.5 -end - -if rotationMode == "Легкое вращение" then - rotationMode = 5 -elseif rotationMode == "Быстрое вращение" then - rotationMode = 15 -elseif rotationMode == "Турбина" then - rotationMode = 25 -elseif rotationMode == "Дюраселл" then - rotationMode = 35 -elseif rotationMode == "Без вращения" then - rotationMode = 0 -end - -if buttonPress == "Отмена" then return end - ------------------------------------------------------------ - --- создаем модель елки -local tSpruce = {3, 2, 2, 2, 2, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 4, 6, 8, 7, 6, 5, 4, 3, 6, 5, 4, 3, 2, 3, 2, 1} - --- создаем таблицу с падающими снежинками -local tSnow = {} - --- создаем палитру цветов -hologram.setPaletteColor(1, 0xFFFFFF) -- снег -hologram.setPaletteColor(2, 0x221100) -- ствол -hologram.setPaletteColor(3, 0x005522) -- хвоя - -local function cricle(x0, y, z0, R, i) -- задействуем алгоритм Брезенхэма для рисования кругов - local x = R - local z = 0 - local err = -R - while z <= x do - hologram.set(x + x0, y, z + z0, i) - hologram.set(z + x0, y, x + z0, i) - hologram.set(-x + x0, y, z + z0, i) - hologram.set(-z + x0, y, x + z0, i) - hologram.set(-x + x0, y, -z + z0, i) - hologram.set(-z + x0, y, -x + z0, i) - hologram.set(x + x0, y, -z + z0, i) - hologram.set(z + x0, y, -x + z0, i) - z = z + 1 - if err <= 0 then - err = err + (2 * z + 1) - else - x = x - 1 - err = err + (2 * (z - x) + 1) - end - end -end - -local function spruce() -- рисуем ель - for i = 1, 5 do - cricle(c, i, c, tSpruce[i], 2) -- отрисовываем основание ствола - cricle(c, i, c, tSpruce[i]-1, 2) - end - for j = 5, #tSpruce do - cricle(c, j, c, tSpruce[j]-1, 3) -- отрисовываем хвою - cricle(c, j, c, tSpruce[j]-2, 3) - end -end - -local function gen_snow() -- генерируем снежинку - local x, y, z = math.random(1, 46), 32, math.random(1, 46) - table.insert(tSnow,{x=x,y=y,z=z}) - hologram.set(x, y, z, 1) -end - -local function falling_snow() -- сдвигаем снежинки вниз - local i=1 - while i<=#tSnow do - if tSnow[i].y>1 then - local x,y,z=tSnow[i].x+math.random(-1, 1), tSnow[i].y-1, tSnow[i].z+math.random(-1, 1) - if x<1 then x=1 end - if x>46 then x=46 end - if z<1 then z=1 end - if z>46 then z=46 end - c=hologram.get(x, y, z) - if c==0 or c==1 then - hologram.set(tSnow[i].x, tSnow[i].y, tSnow[i].z, 0) - tSnow[i].x, tSnow[i].y, tSnow[i].z=x,y,z - hologram.set(x, y, z, 1) - i=i+1 - else - table.remove(tSnow,i) - end - else - table.remove(tSnow,i) - end - os.sleep(snowMode) - end -end - -ecs.info("auto", "auto", "", "Счастливого нового года!") - -hologram.clear() -hologram.setScale(scale) -hologram.setRotationSpeed(rotationMode, 0, 23, 0) - -spruce() -while 1 do - gen_snow() - falling_snow() -end \ No newline at end of file diff --git a/Applications/ChristmasTree/Icon.pic b/Applications/ChristmasTree/Icon.pic deleted file mode 100644 index ac91b1777a6ecb3089b553e9b68394087e4b1ff4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 152 zcmXZUu?+%23(5sulFa}MteLplAwlH;KhzT9B( RW??z*ln 0 then - if ecs.clickedAtArea(e2[3], e2[4], buttons["*"][1], buttons["*"][2], buttons["*"][3], buttons["*"][4]) then - pressButton(buttons["*"][1], buttons["*"][2], "*") - return true - end - end - return false -end - -local function redstone(go) - if go then - rs.setOutput(sides.top, 15) - local goexit = waitForExit() - rs.setOutput(sides.top, 0) - rs.setOutput(sides.bottom, 0) - return goexit - else - rs.setOutput(sides.bottom, 15) - os.sleep(2) - rs.setOutput(sides.top, 0) - rs.setOutput(sides.bottom, 0) - end - return false -end - ------------------------------------------------------------------------------------------------------------- - -ecs.prepareToExit(colors.background) -loadConfig() - -local oldWidth, oldHeight = gpu.getResolution() -gpu.setResolution(34, 17) -xSize, ySize = 34, 17 - -drawAll() -infoPanel("Введите пароль", colors.borders, colors.background) - -while true do - local e = {event.pull()} - if e[1] == "touch" then - for key in pairs(buttons) do - if ecs.clickedAtArea(e[3], e[4], buttons[key][1], buttons[key][2], buttons[key][3], buttons[key][4]) then - - if showKeyPresses then - pressButton(buttons[key][1], buttons[key][2], key) - end - - if key == "*" then - input = nil - infoPanel("Поле ввода очищено", colors.borders, colors.background) - elseif key == "#" then - drawAll() - if input == password then - infoPanel("Доступ разрешён!", ecs.colors.green, 0xFFFFFF) - local goexit = redstone(true) - for i = 1, #nicknames do - if nicknames[i] == e[6] then nicknames[i] = nil end - end - table.insert(nicknames, e[6]) - saveConfig() - - if goexit then - ecs.prepareToExit() - gpu.setResolution(oldWidth, oldHeight) - ecs.prepareToExit() - return - end - else - infoPanel("Доступ запрещён!", ecs.colors.red, 0xFFFFFF) - redstone(false) - end - infoPanel("Введите пароль", colors.borders, colors.background) - input = nil - else - input = (input or "") .. key - - infoPanel(input, colors.borders, colors.background, not showPassword) - end - drawAll() - - break - end - end - - if ecs.clickedAtArea(e[3], e[4], biometry[1], biometry[2], biometry[3], biometry[4]) then - visualScan(biometry[1], biometry[2] + 4, 0.08) - if checkNickname(e[6]) then - infoPanel("Привет, " .. e[6], ecs.colors.green, 0xFFFFFF) - redstone(true) - else - infoPanel("В доступе отказано!", ecs.colors.red, 0xFFFFFF) - redstone(false) - end - infoPanel("Введите пароль", colors.borders, colors.background) - drawAll() - end - end -end - - - - - - - - diff --git a/Applications/CodeDoor/Icon.pic b/Applications/CodeDoor/Icon.pic deleted file mode 100644 index e17de0ae2579dbe8cbea4731b415996359d0507e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 115 zcmeZw_H<+8U}0on;80-HWMp7rclpVwqL)dD{rcij4XDhVj<=&;{L>`|V6BdYrMJ;8&Hm?_J2j+Ab PlV1kpZGg=Ka2ue1Ba<1Y literal 0 HcmV?d00001 diff --git a/Applications/Control/Localization/English.lang b/Applications/Control.app/Localizations/English.lang similarity index 100% rename from Applications/Control/Localization/English.lang rename to Applications/Control.app/Localizations/English.lang diff --git a/Applications/Control/Localization/French.lang b/Applications/Control.app/Localizations/French.lang similarity index 100% rename from Applications/Control/Localization/French.lang rename to Applications/Control.app/Localizations/French.lang diff --git a/Applications/Control/Localization/Russian.lang b/Applications/Control.app/Localizations/Russian.lang similarity index 100% rename from Applications/Control/Localization/Russian.lang rename to Applications/Control.app/Localizations/Russian.lang diff --git a/Applications/Control.app/Main.lua b/Applications/Control.app/Main.lua new file mode 100644 index 00000000..f4a13e25 --- /dev/null +++ b/Applications/Control.app/Main.lua @@ -0,0 +1,58 @@ + +local image = require("Image") +local screen = require("Screen") +local GUI = require("GUI") +local filesystem = require("Filesystem") +local paths = require("Paths") +local system = require("System") + +---------------------------------------------------------------------------------------------------------------- + +local currentScriptDirectory = filesystem.path(system.getCurrentScript()) +local modulesPath = currentScriptDirectory .. "Modules/" +local localization = system.getLocalization(currentScriptDirectory .. "Localizations/") + +local workspace, window = system.addWindow(GUI.tabbedWindow(1, 1, 80, 25)) + +---------------------------------------------------------------------------------------------------------------- + +window.contentContainer = window:addChild(GUI.container(1, 4, window.width, window.height - 3)) + +local function loadModules() + local fileList = filesystem.list(modulesPath) + for i = 1, #fileList do + if filesystem.extension(fileList[i]) == ".lua" then + local loadedFile, reason = loadfile(modulesPath .. fileList[i]) + if loadedFile then + local pcallSuccess, reason = pcall(loadedFile, workspace, window, localization) + if pcallSuccess then + window.tabBar:addItem(reason.name).onTouch = function() + reason.onTouch() + workspace:draw() + end + else + error("Failed to call loaded module \"" .. tostring(fileList[i]) .. "\": " .. tostring(reason)) + end + else + error("Failed to load module \"" .. tostring(fileList[i]) .. "\": " .. tostring(reason)) + end + end + end +end + +window.onResize = function(width, height) + window.tabBar.width = width + window.backgroundPanel.width = width + window.backgroundPanel.height = height - 3 + window.contentContainer.width = width + window.contentContainer.height = window.backgroundPanel.height + + window.tabBar:getItem(window.tabBar.selectedItem).onTouch() +end + +---------------------------------------------------------------------------------------------------------------- + +loadModules() +window.onResize(80, 25) + + diff --git a/Applications/Control/Modules/1.lua b/Applications/Control.app/Modules/1.lua similarity index 79% rename from Applications/Control/Modules/1.lua rename to Applications/Control.app/Modules/1.lua index 9013f70d..aa169762 100644 --- a/Applications/Control/Modules/1.lua +++ b/Applications/Control.app/Modules/1.lua @@ -1,16 +1,12 @@ local args = {...} -local application, window, localization = args[1], args[2], args[3] +local workspace, window, localization = args[1], args[2], args[3] -require("advancedLua") -local component = require("component") -local computer = require("computer") local GUI = require("GUI") -local buffer = require("doubleBuffering") -local image = require("image") -local MineOSPaths = require("MineOSPaths") -local MineOSInterface = require("MineOSInterface") -local unicode = require("unicode") +local screen = require("Screen") +local image = require("Image") +local paths = require("Paths") +local text = require("Text") ---------------------------------------------------------------------------------------------------------------- @@ -22,9 +18,6 @@ module.name = localization.moduleLua module.onTouch = function() window.contentContainer:removeChildren() - _G.component = require("component") - _G.computer = require("computer") - local textBox = window.contentContainer:addChild(GUI.textBox(1, 1, window.contentContainer.width, window.contentContainer.height - 3, nil, 0x444444, localization.luaInfo, 1, 2, 1)) textBox.scrollBarEnabled = true @@ -33,7 +26,7 @@ module.onTouch = function() input.textDrawMethod = function(x, y, color, text) if text == placeholder then - buffer.drawText(x, y, color, text) + screen.drawText(x, y, color, text) else GUI.highlightString(x, y, input.width - 2, 1, 2, GUI.LUA_SYNTAX_PATTERNS, GUI.LUA_SYNTAX_COLOR_SCHEME, text) end @@ -41,7 +34,7 @@ module.onTouch = function() local function add(data, color) for line in data:gmatch("[^\n]+") do - local wrappedLine = string.wrap(line, textBox.textWidth) + local wrappedLine = text.wrap(line, textBox.textWidth) for i = 1, #wrappedLine do table.insert(textBox.lines, color and {color = color, text = wrappedLine[i]} or wrappedLine[i]) end @@ -61,7 +54,7 @@ module.onTouch = function() local args = {...} for i = 1, #args do if type(args[i]) == "table" then - args[i] = table.toString(args[i], true, 2, false, 2) + args[i] = text.serialize(args[i], true, 2, false, 2) else args[i] = tostring(args[i]) end diff --git a/Applications/Control/Modules/2.lua b/Applications/Control.app/Modules/2.lua similarity index 74% rename from Applications/Control/Modules/2.lua rename to Applications/Control.app/Modules/2.lua index 76f3d4d2..1ba74349 100644 --- a/Applications/Control/Modules/2.lua +++ b/Applications/Control.app/Modules/2.lua @@ -1,41 +1,40 @@ local args = {...} -local application, window, localization = args[1], args[2], args[3] +local workspace, window, localization = args[1], args[2], args[3] -require("advancedLua") -local component = require("component") -local computer = require("computer") local GUI = require("GUI") -local buffer = require("doubleBuffering") -local image = require("image") -local MineOSPaths = require("MineOSPaths") -local MineOSInterface = require("MineOSInterface") +local screen = require("Screen") +local image = require("Image") +local paths = require("Paths") +local number = require("Number") ---------------------------------------------------------------------------------------------------------------- local module = {} module.name = localization.moduleDisk -local HDDImage = image.load(MineOSPaths.icons .. "HDD.pic") -local floppyImage = image.load(MineOSPaths.icons .. "Floppy.pic") +local HDDImage = image.load(paths.system.icons .. "HDD.pic") +local floppyImage = image.load(paths.system.icons .. "Floppy.pic") ---------------------------------------------------------------------------------------------------------------- module.onTouch = function() window.contentContainer:removeChildren() local container = window.contentContainer:addChild(GUI.container(1, 1, window.contentContainer.width, window.contentContainer.height)) - + + local eeprom = component.proxy(component.list("eeprom")()) + local y = 2 for address in component.list("filesystem") do local proxy = component.proxy(address) - local isBoot = computer.getBootAddress() == proxy.address + local isBoot = eeprom.getData() == proxy.address local isReadOnly = proxy.isReadOnly() local diskContainer = container:addChild(GUI.container(1, y, container.width, 4)) local button = diskContainer:addChild(GUI.adaptiveRoundedButton(1, 3, 2, 0, 0x2D2D2D, 0xE1E1E1, 0x0, 0xE1E1E1, localization.options)) button.onTouch = function() - local container = MineOSInterface.addBackgroundContainer(application, localization.options) + local container = system.addBackgroundContainer(workspace, localization.options) local inputField = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x666666, 0x666666, 0xE1E1E1, 0x2D2D2D, proxy.getLabel() or "", localization.diskLabel)) inputField.onInputFinished = function() if inputField.text and inputField.text:len() > 0 then @@ -61,14 +60,14 @@ module.onTouch = function() local switch = container.layout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0x1E1E1E, 0xE1E1E1, 0xBBBBBB, localization.bootable .. ":", isBoot)).switch switch.onStateChanged = function() if switch.state then - computer.setBootAddress(proxy.address) + eeprom.setData(proxy.address) container:remove() module.onTouch() end end - application:draw() + workspace:draw() end button.localX = diskContainer.width - button.width - 1 @@ -80,19 +79,19 @@ module.onTouch = function() diskContainer:addChild(GUI.image(3, 1, isReadOnly and floppyImage or HDDImage)) diskContainer:addChild(GUI.label(x, 1, width, 1, 0x2D2D2D, (proxy.getLabel() or "Unknown") .. " (" .. (isBoot and (localization.bootable .. ", ") or "") .. proxy.address .. ")")) diskContainer:addChild(GUI.progressBar(x, 3, width, 0x66DB80, 0xD2D2D2, 0xD2D2D2, spaceUsed / spaceTotal * 100, true)) - diskContainer:addChild(GUI.label(x, 4, width, 1, 0xBBBBBB, localization.free .. " " .. math.roundToDecimalPlaces((spaceTotal - spaceUsed) / 1024 / 1024, 2) .. " MB " .. localization.of .. " " .. math.roundToDecimalPlaces(spaceTotal / 1024 / 1024, 2) .. " MB")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + diskContainer:addChild(GUI.label(x, 4, width, 1, 0xBBBBBB, localization.free .. " " .. number.roundToDecimalPlaces((spaceTotal - spaceUsed) / 1024 / 1024, 2) .. " MB " .. localization.of .. " " .. number.roundToDecimalPlaces(spaceTotal / 1024 / 1024, 2) .. " MB")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) y = y + diskContainer.height + 1 end - container.eventHandler = function(application, object, e1, e2, e3, e4, e5) + container.eventHandler = function(workspace, object, e1, e2, e3, e4, e5) if e1 == "scroll" then if e5 < 0 or container.children[1].localY < 2 then for i = 1, #container.children do container.children[i].localY = container.children[i].localY + e5 end - application:draw() + workspace:draw() end elseif e1 == "component_added" or e1 == "component_removed" and e3 == "filesystem" then module.onTouch() diff --git a/Applications/Control/Modules/3.lua b/Applications/Control.app/Modules/3.lua similarity index 89% rename from Applications/Control/Modules/3.lua rename to Applications/Control.app/Modules/3.lua index d823b8da..c4e37e81 100644 --- a/Applications/Control/Modules/3.lua +++ b/Applications/Control.app/Modules/3.lua @@ -1,16 +1,12 @@ local args = {...} -local application, window, localization = args[1], args[2], args[3] +local workspace, window, localization = args[1], args[2], args[3] -require("advancedLua") -local component = require("component") -local computer = require("computer") local GUI = require("GUI") -local buffer = require("doubleBuffering") -local image = require("image") -local MineOSPaths = require("MineOSPaths") -local MineOSInterface = require("MineOSInterface") -local unicode = require("unicode") +local screen = require("Screen") +local image = require("Image") +local paths = require("Paths") +local text = require("Text") ---------------------------------------------------------------------------------------------------------------- @@ -76,8 +72,8 @@ module.onTouch = function() end end - local function out(text) - local wrappedLines = string.wrap(text, outputTextBox.width - 2) + local function out(t) + local wrappedLines = text.wrap(t, outputTextBox.width - 2) for i = 1, #wrappedLines do table.insert(outputTextBox.lines, wrappedLines[i]) end @@ -114,7 +110,7 @@ module.onTouch = function() local success, reason = pcall(success, tree.selectedItem.value) if success then if type(reason) == "table" then - local serialized = table.toString(reason, true, 2, false, 3) + local serialized = text.serialize(reason, true, 2, false, 3) for line in serialized:gmatch("[^\n]+") do out(line) end @@ -128,7 +124,7 @@ module.onTouch = function() out("Failed to load string \"" .. data .. "\": " .. reason) end - application:draw() + workspace:draw() end diff --git a/Applications/Control/Modules/4.lua b/Applications/Control.app/Modules/4.lua similarity index 75% rename from Applications/Control/Modules/4.lua rename to Applications/Control.app/Modules/4.lua index 29294d7d..a16ec37f 100644 --- a/Applications/Control/Modules/4.lua +++ b/Applications/Control.app/Modules/4.lua @@ -1,15 +1,12 @@ local args = {...} -local application, window, localization = args[1], args[2], args[3] +local workspace, window, localization = args[1], args[2], args[3] -require("advancedLua") -local component = require("component") -local computer = require("computer") local GUI = require("GUI") -local buffer = require("doubleBuffering") -local image = require("image") -local MineOSPaths = require("MineOSPaths") -local MineOSInterface = require("MineOSInterface") +local screen = require("Screen") +local image = require("Image") +local paths = require("Paths") +local text = require("Text") ---------------------------------------------------------------------------------------------------------------- @@ -30,17 +27,17 @@ module.onTouch = function() local textBox = layout:addChild(GUI.textBox(1, 1, container.width - 4, container.height - 4, nil, 0x888888, {localization.waitingEvents .. "..."}, 1, 0, 0)) local switch = layout:addChild(GUI.switchAndLabel(1, 1, 27, 6, 0x66DB80, 0x1E1E1E, 0xFFFFFF, 0x2D2D2D, localization.processingEnabled .. ": ", true)).switch - textBox.eventHandler = function(application, object, ...) + textBox.eventHandler = function(workspace, object, ...) local eventData = {...} if switch.state and eventData[1] then local lines = table.concat(eventData, " ") - lines = string.wrap(lines, textBox.width) + lines = text.wrap(lines, textBox.width) for i = 1, #lines do table.insert(textBox.lines, lines[i]) end textBox:scrollToEnd() - application:draw() + workspace:draw() end end end diff --git a/Applications/Control/Icon.pic b/Applications/Control/Icon.pic deleted file mode 100644 index 748c38bbe4f53287434491ddf0d9e1bf37545432..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 168 zcmXZTu?+$-425Ao$4)#;N>(@tiHhdNKz9;i03;ZK(y$0qus{}p9C7#A{(YPd`)tBr z(&i5kK9B?{&2MAil$&2cTp5YAr>p{jwdeg`U@KleyuA|Hf{Gdaq4~R`B3aKwbFp2V SY#m_E=UC|}P%Z(P0{8(gG8y9l diff --git a/Applications/Control/Main.lua b/Applications/Control/Main.lua deleted file mode 100644 index c35d5967..00000000 --- a/Applications/Control/Main.lua +++ /dev/null @@ -1,61 +0,0 @@ - -require("advancedLua") -local component = require("component") -local computer = require("computer") -local image = require("image") -local buffer = require("doubleBuffering") -local GUI = require("GUI") -local fs = require("filesystem") -local unicode = require("unicode") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") -local MineOSInterface = require("MineOSInterface") - ----------------------------------------------------------------------------------------------------------------- - -local resourcesPath = MineOSCore.getCurrentScriptDirectory() -local modulesPath = resourcesPath .. "Modules/" -local localization = MineOSCore.getLocalization(resourcesPath .. "Localizations/") - -local application, window = MineOSInterface.addWindow(GUI.tabbedWindow(1, 1, 80, 25)) - ----------------------------------------------------------------------------------------------------------------- - -window.contentContainer = window:addChild(GUI.container(1, 4, window.width, window.height - 3)) - -local function loadModules() - local fileList = fs.sortedList(modulesPath, "name", false) - for i = 1, #fileList do - local loadedFile, reason = loadfile(modulesPath .. fileList[i]) - if loadedFile then - local pcallSuccess, reason = pcall(loadedFile, application, window, localization) - if pcallSuccess then - window.tabBar:addItem(reason.name).onTouch = function() - reason.onTouch() - MineOSInterface.application:draw() - end - else - error("Failed to call loaded module \"" .. tostring(fileList[i]) .. "\": " .. tostring(reason)) - end - else - error("Failed to load module \"" .. tostring(fileList[i]) .. "\": " .. tostring(reason)) - end - end -end - -window.onResize = function(width, height) - window.tabBar.width = width - window.backgroundPanel.width = width - window.backgroundPanel.height = height - 3 - window.contentContainer.width = width - window.contentContainer.height = window.backgroundPanel.height - - window.tabBar:getItem(window.tabBar.selectedItem).onTouch() -end - ----------------------------------------------------------------------------------------------------------------- - -loadModules() -window.onResize(80, 25) - - diff --git a/Applications/Crossword/About/English.txt b/Applications/Crossword/About/English.txt deleted file mode 100644 index 46c1d5e8..00000000 --- a/Applications/Crossword/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Программа для автоматического решения кроссворда, где вам необходимо искать среди букв указанные слова. Сам файл кроссворда располагается в папке приложения. \ No newline at end of file diff --git a/Applications/Crossword/About/Russian.txt b/Applications/Crossword/About/Russian.txt deleted file mode 100644 index 46c1d5e8..00000000 --- a/Applications/Crossword/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Программа для автоматического решения кроссворда, где вам необходимо искать среди букв указанные слова. Сам файл кроссворда располагается в папке приложения. \ No newline at end of file diff --git a/Applications/Crossword/Crossword.lua b/Applications/Crossword/Crossword.lua deleted file mode 100644 index c7fef5c2..00000000 --- a/Applications/Crossword/Crossword.lua +++ /dev/null @@ -1,294 +0,0 @@ -local component = require("component") -local term = require("term") -local unicode = require("unicode") -local event = require("event") -local fs = require("filesystem") -local ecs = require("ECSAPI") -local gpu = component.gpu - ------------------------------------------------------------------------------------------------------------------------- - -local arg = {...} - -local crossword = {} -local keyWords = {} -local keyWordsColors = {} - -local temporaryWordTrace = {} -local globalWordTrace = {} - ------------------------------------------------------------------------------------------------------------------------- - -local function readFile(path) - --ЧИТАЕМ И ЗАПОЛНЯЕМ МАССИВ - local lines = {} - local f = io.open(path, "r") - while true do - local line = f:read("*l") - if line then - table.insert(lines, unicode.upper(line)) - else - break - end - end - f:close() - - return lines -end - - -local function getCrosswordFromFile(pathToCrossword) - - --ЧИТАЕМ ФАЙЛ КРОССВОРДА - local readedCrossword = readFile(pathToCrossword) - - --ЕСЛИ ПЕРВАЯ ФРАЗА В ФАЙЛЕ НЕ РАВНА НУЖНОЙ, ТО КИНУТЬ ОШИБКУ, А ТО МАЛО ЛИ - if readedCrossword[1] ~= "--КРОССВОРД" then ecs.error("Ошибка чтения файла кроссворда: скорее всего, он хуево составлен. Давай заново.") end - - --ПАРСИМ КРОССВОРД НА БУКОВКИ - local keyWordsStarted = false - for i = 2, #readedCrossword do - --ЕСЛИ НАЙДЕНА ФРАЗА НУЖНАЯ, ТО ВКЛЮЧИТЬ РЕЖИМ ЧТЕНИЯ КЛЮЧЕВЫХ СЛОВ - if readedCrossword[i] == "--КЛЮЧЕВЫЕ СЛОВА" then - keyWordsStarted = true - end - --ВЫБОР МЕЖДУ РЕЖИМАМИ КРОССВОРДА И КЛЮЧЕВЫХ СЛОВ - if not keyWordsStarted then - local position = #crossword + 1 - crossword[position] = {} - for j = 1, unicode.len( readedCrossword[i] ) do - crossword[position][j] = { unicode.sub(readedCrossword[i], j, j) } - end - else - local yPos = i + 1 - if readedCrossword[yPos] then - local position = #keyWords + 1 - keyWords[position] = {} - for j = 1, unicode.len( readedCrossword[yPos] ) do - table.insert( keyWords[position], unicode.sub(readedCrossword[yPos], j, j) ) - end - else - break - end - end - end - - -end - -local function drawCrossword(x, y, background, foreground, xSpaceBetween, ySpaceBetween) - - --ЗАДАНИЕ СТАРТОВЫХ АРГУМЕНТОВ, А ТО МАЛО ЛИ ЧЁ - x = x or 1 - y = y or 1 - - background = background or 0xffffff - foreground = foreground or 0x000000 - - local barsColor = 0xaaaaaa - local barsTextColor = 0xffffff - - xSpaceBetween = 3 - ySpaceBetween = 1 - - --КОРРЕКЦИЯ КООРДИНАТ - local xPos = x - local yPos = y - - --РИСУЕМ РЯДЫ - ecs.square(xPos, yPos, xSpaceBetween + #crossword[1] + xSpaceBetween * #crossword[1] - 1, 1, barsColor) - ecs.square(xPos, yPos, 2, ySpaceBetween + #crossword + ySpaceBetween * #crossword, barsColor) - - xPos = x + xSpaceBetween + 1 - yPos = yPos + ySpaceBetween + 1 - - --РИСУЕМ САМ КРОССВОРД - - for i = 1, #crossword do - - for j = 1, #crossword[i] do - local cvet1 = background - local cvet2 = foreground - if crossword[i][j][2] then - cvet1 = crossword[i][j][2] - cvet2 = 0xffffff - cvet1 - end - - gpu.setBackground(cvet1) - gpu.setForeground(cvet2) - - gpu.set(xPos, yPos, crossword[i][j][1]) - - --РИСУЕМ ТЕКСТ НА ПОЛОСОЧКАХ С НОМЕРАМИ - gpu.setForeground(barsTextColor) - gpu.setBackground(barsColor) - - gpu.set(xPos, y, tostring(j)) - gpu.set(x, yPos, tostring(i)) - - xPos = xPos + xSpaceBetween + 1 - --event.pull("key_down") - end - xPos = x + xSpaceBetween + 1 - yPos = yPos + ySpaceBetween + 1 - end - - --СЛОВЕЧКИ РИСУЕМ - - gpu.setBackground(background) - gpu.setForeground(foreground) - - --ЛИНИЮ РИСУЕМ - xPos = x + xSpaceBetween - gpu.set(1, yPos, string.rep("-", 100)) - yPos = yPos + ySpaceBetween + 1 - - for i = 1, #keyWords do - - local color1, color2 = background, foreground - if keyWordsColors[i] then color1 = keyWordsColors[i]; color2 = 0xffffff - color1 end - - local slovo = "" - for j = 1, #keyWords[i] do - slovo = slovo .. keyWords[i][j] - end - - gpu.setBackground(color1) - gpu.setForeground(color2) - - gpu.set(xPos, yPos, slovo) - --ecs.error("Слово = "..slovo) - - yPos = yPos + 1 - end -end - -local function findWord(x, y, wordCyka, uspeh ) - - if #wordCyka <= 1 then return uspeh end - - local word = {} - for i = 1, #wordCyka do - word[i] = wordCyka[i] - end - - uspeh = false - - table.remove(word, 1) - - --ecs.error("#wordCyka="..#wordCyka..", #word="..#word) - - --ecs.error("Рассматриваю х="..x..", y="..y..", word[1]="..word[1]..", xOtkuda="..xOtkuda..", yOtkuda="..yOtkuda) - - local function cyka(xMod, yMod) - --ecs.error(word[1] .." ".. crossword[y + yMod][x + xMod][1]) - if word[1] == crossword[y + yMod][x + xMod][1] and not uspeh then - --ecs.error("Смотрю на ("..x..","..y..","..crossword[y][x][1].."), ищу вокруг букву \""..word[1].."\", #свет="..#temporaryWordTrace) - - uspeh = true - table.insert(temporaryWordTrace, {xMod, yMod}) - - uspeh = findWord( x + xMod, y + yMod, word, uspeh ) - end - end - - if crossword[y - 1] then cyka(0, -1) end - if crossword[y][x + 1] then cyka(1, 0) end - if crossword[y + 1] then cyka(0, 1) end - if crossword[y][x - 1] then cyka(-1, 0) end - - --УБИРАЕМ ЭЛЕМЕНТИК ИЗ ПОДСВЕТКИ - if not uspeh then temporaryWordTrace[#temporaryWordTrace] = nil end - - --ecs.error("Успех в конце = "..tostring(uspeh)) - return uspeh -end - -local function trace(massivSuda) - local nomerSlova, x, y, track = massivSuda[1], massivSuda[2], massivSuda[3], massivSuda[4] - - local color = math.random(0x000000, 0xffffff) - --ecs.error("x="..x..", y="..y) - - keyWordsColors[nomerSlova] = color - - crossword[y][x][2] = color - - for i = 1, #track do - x = x + track[i][1] - y = y + track[i][2] - - crossword[y][x][2] = color - end -end - -local function reshitCrossword() - for ySimvol = 1, #crossword do - for xSimvol = 1, #crossword[ySimvol] do - - local yKeyWord = 1 - while yKeyWord <= #keyWords do - - --ЕСЛИ ПЕРВАЯ БУКВА КЛЮЧЕВОГО СЛОВА РАВНА СИМВОЛУ РАССМАТРИВАЕМОМУ - if keyWords[yKeyWord][1] == crossword[ySimvol][xSimvol][1] then - - --ДУБЛИРОВАНИЕ МАССИВА, ПОТОМУШТО ТАК НУЖНО!!! ВОТ - -- local cyka = {} - -- for i = 1, #keyWords do - -- cyka[i] = {} - -- for j = 1, #keyWords[i] do - -- cyka[i][j] = keyWords[i][j] - -- end - -- end - - --table.remove(cyka[yKeyWord], 1) - - --ecs.error("#cyka[yKeyWord] = "..#cyka[yKeyWord]..", #keyWords[yKeyWord] = "..#keyWords[yKeyWord]) - - --yKeyWord = 3 - - temporaryWordTrace = {} - --ecs.error("Начинаю поиск слова №"..yKeyWord.." на x = "..xSimvol..", y = "..ySimvol) - if findWord(xSimvol, ySimvol, keyWords[yKeyWord], true ) then - - - --ecs.error("УСПЕХ! НАШЛО СЛОВО НОМЕР " .. yKeyWord .. ", его размер="..#keyWords[yKeyWord]..", #свет="..#temporaryWordTrace) - - table.insert(globalWordTrace, {yKeyWord, xSimvol, ySimvol, temporaryWordTrace}) - - trace(globalWordTrace[#globalWordTrace]) - - --drawCrossword(2, 2, 0xeeeeee, 0x333333, 3, 1) - - yKeyWord = yKeyWord + 1 - - --event.pull("key_down") - end - - end - - yKeyWord = yKeyWord + 1 - end - end - end -end - - ------------------------------------------------------------------------------------------------------------------------- - -ecs.clearScreen(0x262626) - -local vvod = ecs.universalWindow("auto", "auto", 40, 0xeeeeee, true, {"EmptyLine"}, {"CenterText", 0x262626, "Путь к файлу кроссворда"}, {"EmptyLine"}, {"Input", 0x262626, ecs.colors.green, "MineOS/Applications/Crossword.app/Resources/CrosswordFile.txt"}, {"EmptyLine"}, {"Button", {ecs.colors.green, 0xffffff, "OK"}}) -local pathToCrossword = vvod[1] -local mode = vvod[2] - -ecs.clearScreen(0xeeeeee) -getCrosswordFromFile(pathToCrossword) -drawCrossword(2, 2, 0xeeeeee, 0x333333, 3, 1) -reshitCrossword() - -event.pull("key_down") -drawCrossword(2, 2, 0xeeeeee, 0x333333, 3, 1) - -os.sleep(1) -event.pull("key_down") diff --git a/Applications/Crossword/CrosswordFile.txt b/Applications/Crossword/CrosswordFile.txt deleted file mode 100644 index f5600250..00000000 --- a/Applications/Crossword/CrosswordFile.txt +++ /dev/null @@ -1,12 +0,0 @@ ---КРОССВОРД -черттевпосетедсанморпм -лиеёиосьлетинжассиапко -окижтельоицеирмуньттек -ваннкомспзиябоотносьра -кодиломопотаобпонрфунк ---КЛЮЧЕВЫЕ СЛОВА -чертёж -осветитель -посетитель -компромисс -насаждение diff --git a/Applications/Crossword/Icon.pic b/Applications/Crossword/Icon.pic deleted file mode 100644 index 66219de85c4ec79eba6e8205d3181922f6094894..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 308 zcmYL_OAf*?3`9G&Q@Z8^T!GI804WkH7Fp$1fIvbBArS1i3^yv{R%w^-C1Z`f-|nI{ zw#Kwd_`M>&n+k0f2{rlIv9uYY8RqISuV$`#ky&zO*&WgoIf{#XF>I NfClgJ?lZiEhCe(kN2UM( diff --git a/Applications/DanceFloor/About/English.txt b/Applications/DanceFloor/About/English.txt deleted file mode 100644 index 2281696d..00000000 --- a/Applications/DanceFloor/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Виртуальный танцпол, реагирующий на хождение по экрану и на клики по нему. Отлично войдет в интерьер какого-нибудь ночного клуба, если, конечно, в майне с его юной аудиторией вообще может быть пиздатый ночной клуб. Ну да ладно, пользуйтесь на здоровье. Кстати, прога частично спизжена с русской майн-вики. \ No newline at end of file diff --git a/Applications/DanceFloor/About/Russian.txt b/Applications/DanceFloor/About/Russian.txt deleted file mode 100644 index 2281696d..00000000 --- a/Applications/DanceFloor/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Виртуальный танцпол, реагирующий на хождение по экрану и на клики по нему. Отлично войдет в интерьер какого-нибудь ночного клуба, если, конечно, в майне с его юной аудиторией вообще может быть пиздатый ночной клуб. Ну да ладно, пользуйтесь на здоровье. Кстати, прога частично спизжена с русской майн-вики. \ No newline at end of file diff --git a/Applications/DanceFloor/DanceFloor.lua b/Applications/DanceFloor/DanceFloor.lua deleted file mode 100644 index b59ca775..00000000 --- a/Applications/DanceFloor/DanceFloor.lua +++ /dev/null @@ -1,212 +0,0 @@ -local component = require("component") -local event = require("event") -local gpu = component.gpu -local ecs = require("ECSAPI") - -local xOld, yOld = gpu.getResolution() -local xSize, ySize -local circles = {} -local bg, fg, mode, speed - ---============================ Ф У Н К Ц И И ==============================-- - -local function tanci() - -- отрисовка кругов - local function draw() - gpu.setBackground(bg) - gpu.fill(1, 1, xSize*2, ySize, " ") - - for i=#circles, 1, -1 do - local x = circles[i][1]+1 - local y = circles[i][2]-circles[i][4]+2 - gpu.setBackground(circles[i][3]) - - for c=1, (circles[i][4]-1)*4 do - if x>0 and x<=(xSize*2) then - if y>0 and y<=ySize then - gpu.set((x)*2 - 1, y, " ") - end - end - -- следующий "пиксель" круга - if x>circles[i][1] then - if ycircles[i][2] then - x = x-1 - else - x = x+1 - end - y = y-1 - end - end - - circles[i][4] = circles[i][4] + 1 - if circles[i][4] > xSize then table.remove(circles, i) end - end - end - - while true do - -- обработка сигналов - local e = {event.pull(speed)} - - if e[1] == "touch" then - table.insert(circles, {e[3] / 2, e[4], math.random(0xffffff), 1}) - elseif e[1] == "walk" then - table.insert(circles, {e[3], e[4], math.random(0xffffff), 1}) - elseif e[1] == "key_down" then - if e[4] == 28 then break end - end - - draw() - end -end - -local function shahmati() - local c1 = 0x000000 - local c2 = 0xffffff - for j = 1, ySize do - for i = 1, xSize do - circles[j] = circles[j] or {} - if j % 2 == 0 then - if i % 2 == 0 then - circles[j][i] = c1 - else - circles[j][i] = c2 - end - else - if i % 2 == 0 then - circles[j][i] = c2 - else - circles[j][i] = c1 - end - end - end - end - - local function cyka() - for j = 1, #circles do - for i = 1, #circles[j] do - circles[j][i] = 0xffffff - circles[j][i] - gpu.setBackground(circles[j][i]) - gpu.set(i*2 - 1, j, " ") - end - end - end - - cyka() - - while true do - local e = {event.pull(speed)} - - if e[1] == "touch" then - circles[e[4]][e[3] / 2] = math.random(0xffffff) - elseif e[1] == "walk" then - circles[e[4]][e[3]] = math.random(0xffffff) - elseif e[1] == "key_down" then - if e[4] == 28 then break end - end - - cyka() - end -end - -local function spidi() - local function cyka() - for j = 1, ySize do - for i = 1, xSize do - gpu.setBackground(math.random(0xffffff)) - gpu.set(i*2 - 1, j, " ") - end - end - end - - cyka() - - while true do - local e = {event.pull(speed)} - - if e[1] == "key_down" then - if e[4] == 28 then break end - end - - cyka() - end -end - -local function beg() - local function cyka() - local cyka2 = {bg, fg} - gpu.copy(1,1,xSize*2,ySize, 2, 0) - for j = 1, ySize do - gpu.setBackground(cyka2[math.random(1, 2)]) - gpu.set(1, j, " ") - end - end - - cyka() - - while true do - local e = {event.pull(speed)} - - if e[1] == "key_down" then - if e[4] == 28 then break end - end - - cyka() - end -end - ---===========================================================================-- - -local data = ecs.universalWindow("auto", "auto", 36, 0xeeeeee, true, - {"EmptyLine"}, - {"CenterText", 0x880000, "Танцпол v1.0"}, - {"EmptyLine"}, - {"CenterText", 0x262626, "Реагинует на хождение"}, - {"CenterText", 0x262626, "по экрану и прикосновение к нему,"}, - {"CenterText", 0x262626, "для выхода удерживайте Enter"}, - {"EmptyLine"}, - {"Selector", 0x262626, 0x880000, "ШАХМАТЫ", "СПИДЫ", "ТАНЦЫ", "БЕГ"}, - {"Color", "Цвет 1", 0x000000}, - {"Color", "Цвет 2", 0xFFFFFF}, - {"Slider", 0x262626, 0x880000, 1, 100, 100, "Скорость ", " FPS"}, - {"EmptyLine"}, - {"Button", {0x888888, 0xffffff, "OK"}, {0xaaaaaa, 0xffffff, "Отмена"}} -) - -if data[5] == "OK" then - mode = data[1] - bg = data[2] - fg = data[3] - speed = (102 - tonumber(data[4])) / 100 -else - ecs.prepareToExit() - return -end - -xSize, ySize = component.screen.getAspectRatio() -gpu.setResolution(xSize * 2, ySize) -gpu.fill(1, 1, 16, 6, " ") - -if mode == "СПИДЫ" then - spidi() -elseif mode == "ТАНЦЫ" then - tanci() -elseif mode == "ШАХМАТЫ" then - shahmati() -elseif mode == "БЕГ" then - beg() -end - -gpu.setResolution(xOld, yOld) -ecs.prepareToExit() - - - - - diff --git a/Applications/DanceFloor/Icon.pic b/Applications/DanceFloor/Icon.pic deleted file mode 100644 index 01e581ffdb794c2b75b684620cbd58aeee402771..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79 zcmeZw_H<+8U}0onV0tvIg^7XjKMNxZBQq;I6PRQMlPqBJKO+M(6AKdy8wWF(WC4?p NrZq4Ebulu6c>sY@2>Jj3 diff --git a/Applications/Drone farmer/DroneBIOS.lua b/Applications/Drone farmer/DroneBIOS.lua deleted file mode 100644 index 7b46b301..00000000 --- a/Applications/Drone farmer/DroneBIOS.lua +++ /dev/null @@ -1,43 +0,0 @@ - -drone = component.proxy(component.list("drone")()) -eeprom = component.proxy(component.list("eeprom")()) -modem = component.proxy(component.list("modem")()) -port = 512 -modem.open(port) - -local function executeCode(code) - local loadSuccess, loadReason = load(code) - if loadSuccess then - local xpcallSuccess, xpcallReason = xpcall(loadSuccess, debug.traceback) - if xpcallSuccess then - return true - else - return false, xpcallReason - end - else - return false, loadReason - end -end - -while true do - local eventData = {computer.pullSignal()} - if eventData[1] == "modem_message" then - if eventData[6] == "executeCode" and eventData[7] then - local success, reason = executeCode(table.unpack(eventData, 8)) - if eventData[7] == true then - modem.send(eventData[3], port, "executionResult", success, reason) - end - elseif eventData[6] == "flashEEPROM" and eventData[7] then - local success = load(eventData[7]) - if success then - eeprom.set(eventData[7]) - computer.beep(1000, 1) - computer.stop() - computer.start() - else - for i = 1, 3 do computer.beep(800, 0.3) end - end - end - end -end - diff --git a/Applications/Drone farmer/DroneCode.lua b/Applications/Drone farmer/DroneCode.lua deleted file mode 100644 index d8b40ce9..00000000 --- a/Applications/Drone farmer/DroneCode.lua +++ /dev/null @@ -1,150 +0,0 @@ - -local position = {x = 0, y = 0, z = 0} -local rotation = 0 -local movePrecision = 0.5 - ------------------------------------------------------------------------------------- - -local function sleep(timeout) - local deadline = computer.uptime() + (timeout or 0) - while computer.uptime() < deadline do - computer.pullSignal(deadline - computer.uptime()) - end -end - -local function absoluteSwing(...) - while true do - local success, reason = drone.swing(...) - if success or reason == "air" then break end - end -end - -local function absoluteMove(x, y, z) - drone.move(x, y, z) - while drone.getOffset() > movePrecision do - sleep(0.05) - end - position.x, position.y, position.z = position.x + x, position.y + y, position.z + z -end - -local function relativeMove(x, y, z) - if rotation == 0 then - absoluteMove(x, y, -z) - elseif rotation == 1 then - absoluteMove(z, y, x) - elseif rotation == 2 then - absoluteMove(-x, y, z) - else - absoluteMove(-z, y, -x) - end -end - -local function relativeSwing(preferredRotation) - local front, right, back, left = 4, 2, 5, 3 - if rotation == 0 then - front, right, back, left = 2, 5, 3, 4 - elseif rotation == 1 then - front, right, back, left = 5, 3, 4, 2 - elseif rotation == 2 then - front, right, back, left = 3, 4, 2, 5 - end - - absoluteSwing(select((preferredRotation or 0) + 1, front, right, back, left)) -end - -local function swingForward() - relativeSwing(0) -end - -local function moveForward(distance) - relativeMove(0, 0, distance or 1) -end - -local function moveBackward(distance) - relativeMove(0, 0, distance and -distance or -1) -end - -local function turnLeft() - rotation = rotation - 1 - if rotation < 0 then rotation = 3 end -end - -local function turnRight() - rotation = rotation + 1 - if rotation > 3 then rotation = 0 end -end - -local function moveToPoint(x, y, z) - absoluteMove(x - position.x, y - position.y, z - position.z) -end - -local function returnToStartPoint() - moveToPoint(0, position.y, 0) - moveToPoint(0, 0, 0) -end - ------------------------------------------------------------------------------------- - -local fieldWidth = 18 -local fieldHeight = 18 - -local function dropShitOnBase() - returnToStartPoint() - moveBackward(1) - - computer.beep(1500, 0.3) - for slot = 1, drone.inventorySize() do - drone.select(slot) - drone.drop(0) - end - drone.select(1) -end - -local function checkInventory() - if drone.count(drone.inventorySize()) > 0 then - local xOld, yOld, zOld = position.x, position.y, position.z - dropShitOnBase() - moveToPoint(xOld, yOld, zOld) - end -end - -local function doHeight() - for y = 1, fieldHeight do - swingForward() - sleep(0.1) - moveForward(1) - end - checkInventory() -end - -local function doWidth() - for x = 1, fieldWidth / 2 do - doHeight() - turnRight() - swingForward() - moveForward(1) - turnRight() - - doHeight() - turnLeft() - swingForward() - moveForward(1) - turnLeft() - end -end - -rotation = 3 -drone.setAcceleration(1) - -absoluteMove(0, 0.5, 0) -doWidth() -dropShitOnBase() -returnToStartPoint() - - - - - - - - diff --git a/Applications/Drone farmer/PC.lua b/Applications/Drone farmer/PC.lua deleted file mode 100644 index ad598377..00000000 --- a/Applications/Drone farmer/PC.lua +++ /dev/null @@ -1,29 +0,0 @@ - -local component = require("component") -local event = require("event") -local modem = component.modem -local port = 512 -modem.open(port) - -while true do - local e = {event.pull()} - if e[1] == "key_down" then - if e[4] == 28 then - local file = io.open("/DroneCode.lua", "r") - local data = file:read("*a") - file:close() - modem.broadcast(port, "executeCode", true, data) - elseif e[4] == 14 then - local file = io.open("/DroneBIOS.lua", "r") - local data = file:read("*a") - file:close() - modem.broadcast(port, "flashEEPROM", data) - end - elseif e[1] == "modem_message" then - if e[6] == "executionResult" then - print("Резултат выполнения кода: " .. tostring(e[7]) .. ", " .. tostring(e[8])) - end - end -end - - diff --git a/Applications/DroneGrief/Drone.lua b/Applications/DroneGrief/Drone.lua deleted file mode 100644 index 15465c29..00000000 --- a/Applications/DroneGrief/Drone.lua +++ /dev/null @@ -1,139 +0,0 @@ - -local drone = component.proxy(component.list("drone")()) -local modem = component.proxy(component.list("modem")()) -local inventory = component.proxy(component.list("inventory_controller")()) -local port = 512 -local moveSpeed = 1.0 -local suckSide = 0 -local direction = 0 -local acceleration = 0.5 -local autoDrop = true -local leashed = false - -modem.open(port) - -------------------------------------- - -local function move(forward) - if direction == 0 then - drone.move(1 * forward, 0, 0) - elseif direction == 1 then - drone.move(0, 0, 1 * forward) - elseif direction == 2 then - drone.move(-1 * forward, 0, 0) - else - drone.move(0, 0, -1 * forward) - end -end - -local function printSpeed() - drone.setStatusText("SPD " .. tostring(moveSpeed)) -end - -local function printDirection() - drone.setStatusText("DIR " .. tostring(direction)) -end - -local function printAcceleration() - drone.setStatusText("ACC " .. tostring(acceleration)) -end - -local function sendInfo() - modem.broadcast(port, "ECSDrone", "DroneInfo", moveSpeed, acceleration, direction) -end - -local function dropAll() - for i = 1, drone.inventorySize() do - drone.select(i) - drone.drop(1) - end - drone.select(1) - drone.setStatusText("DROPPED") -end - -drone.setStatusText("STARTED") - -while true do - local e = { computer.pullSignal() } - if e[1] == "modem_message" then - if e[4] == port then - if e[6] == "ECSDrone" then - drone.setStatusText(e[7]) - if e[7] == "moveUp" then - drone.move(0, moveSpeed, 0) - elseif e[7] == "moveDown" then - drone.move(0, -moveSpeed, 0) - elseif e[7] == "moveForward" then - move(1) - elseif e[7] == "moveBack" then - move(-1) - elseif e[7] == "turnLeft" then - direction = direction - 1 - if direction < 0 then direction = 3 end - printDirection() - sendInfo() - elseif e[7] == "turnRight" then - direction = direction + 1 - if direction > 3 then direction = 0 end - printDirection() - sendInfo() - elseif e[7] == "changeColor" then - drone.setLightColor(math.random(0x0, 0xFFFFFF)) - elseif e[7] == "OTSOS" then - for i = 1, (inventory.getInventorySize(0) or 1) do - inventory.suckFromSlot(0, i) - end - drone.setStatusText("SUCKED") - elseif e[7] == "swing" then - drone.swing(0) - elseif e[7] == "dropAll" then - dropAll() - elseif e[7] == "changeAutoDrop" then - changeAutoDrop = not changeAutoDrop - drone.setStatusText("DROP: " .. tostring(changeAutoDrop)) - elseif e[7] == "moveSpeedUp" then - moveSpeed = moveSpeed + 0.1 - if moveSpeed >= 8 then moveSpeed = 8 end - printSpeed() - sendInfo() - elseif e[7] == "moveSpeedDown" then - moveSpeed = moveSpeed - 0.1 - if moveSpeed <= 0.1 then moveSpeed = 0.1 end - printSpeed() - sendInfo() - elseif e[7] == "accelerationUp" then - acceleration = acceleration + 0.1 - if acceleration >= 5 then acceleration = 5 end - drone.setAcceleration(acceleration) - printAcceleration() - sendInfo() - elseif e[7] == "accelerationDown" then - acceleration = acceleration - 0.1 - if acceleration <= 0.1 then acceleration = 0.1 end - drone.setAcceleration(acceleration) - printAcceleration() - sendInfo() - elseif e[7] == "toggleLeash" then - if leashed then - component.proxy(component.list("leash")()).unleash() - leashed = false - else - component.proxy(component.list("leash")()).leash(suckSide) - leashed = true - end - end - end - end - end -end - - - - - - - - - - - diff --git a/Applications/DroneGrief/Planshet.lua b/Applications/DroneGrief/Planshet.lua deleted file mode 100644 index 29f5b16d..00000000 --- a/Applications/DroneGrief/Planshet.lua +++ /dev/null @@ -1,69 +0,0 @@ - -local component = require("component") -local modem = component.modem -local event = require("event") -local keyboard = require("keyboard") -local port = 512 -modem.open(port) - -local keys = { - [17] = "moveForward", - [31] = "moveBack", - [30] = "turnLeft", - [32] = "turnRight", - [42] = "moveDown", - [57] = "moveUp", - [46] = "changeColor", - [18] = "OTSOS", - [16] = "dropAll", - [33] = "toggleLeash", -} - ---------------------------------------------------------------------------------------------------------- - -print(" ") -print("Добро пожаловать в программу DroneGrief. Используйте клавиши W и S для перемещения дрона, а A и D для смены направления движения. По нажатию SHIFT дрон опустится ниже, а по SPACE - выше. Кнопка E заставит дрона высосать предметы из инвентаря под и над ним, а кнопка C сменит цвет его свечения. При скроллинге колесиком мыши изменяется скорость движения робота, а скроллинг с зажатым ALT изменяет его ускорение.") -print(" ") - ---------------------------------------------------------------------------------------------------------- - -while true do - local e = {event.pull()} - if e[1] == "key_down" then - if keys[e[4]] then - print("Команда дрону: " .. keys[e[4]]) - modem.broadcast(port, "ECSDrone", keys[e[4]]) - end - elseif e[1] == "scroll" then - if e[5] == 1 then - if keyboard.isAltDown() then - modem.broadcast(port, "ECSDrone", "accelerationUp") - print("Команда дрону: accelerationUp") - else - modem.broadcast(port, "ECSDrone", "moveSpeedUp") - print("Команда дрону: moveSpeedUp") - end - else - if keyboard.isAltDown() then - modem.broadcast(port, "ECSDrone", "accelerationDown") - print("Команда дрону: accelerationDown") - else - modem.broadcast(port, "ECSDrone", "moveSpeedDown") - print("Команда дрону: moveSpeedDown") - end - end - elseif e[1] == "modem_message" then - if e[6] == "ECSDrone" and e[7] == "DroneInfo" then - print(" ") - print("Скорость дрона: " .. tostring(e[8])) - print("Ускорение дрона: " .. tostring(e[9])) - print("Направление дрона: " .. tostring(e[10])) - print(" ") - end - end -end - - - - - diff --git a/Applications/EEPROMVirus.lua b/Applications/EEPROMVirus.lua deleted file mode 100644 index c8fdd4b1..00000000 --- a/Applications/EEPROMVirus.lua +++ /dev/null @@ -1,322 +0,0 @@ - -local virusPath = "bin/virus.lua" -local EEPROMLabel = "EEPROM (Lua BIOS)" - ------------------------------------------------------------------------------------------------------------------------- - -local EEPROMCode = [[ - -local textLines = { - "Поздравляем!", - "Вы стали одним из первых счастливых обладателей вируса на OpenComputers.", - "Попытайтесь его удалить - посмотрим, что у вас выйдет. ", - "Ну, а нубикам советую обращаться к ECS для разблокировки компа.", - " ", - "Хех)" -} - -local component_invoke = component.invoke -function boot_invoke(address, method, ...) - local result = table.pack(pcall(component_invoke, address, method, ...)) - if not result[1] then - return nil, result[2] - else - return table.unpack(result, 2, result.n) - end -end ---------------------------------------------------------------- -local eeprom = component.list("eeprom")() -computer.getBootAddress = function() - return boot_invoke(eeprom, "getData") -end -computer.setBootAddress = function(address) - return boot_invoke(eeprom, "setData", address) -end - -do - _G.screen = component.list("screen")() - _G.gpu = component.list("gpu")() - if gpu and screen then - boot_invoke(gpu, "bind", screen) - end -end ---------------------------------------------------------------- - -local function centerText(mode,coord,text) - local dlina = unicode.len(text) - local xSize,ySize = boot_invoke(gpu, "getResolution") - - if mode == "x" then - boot_invoke(gpu, "set", math.floor(xSize/2-dlina/2),coord,text) - elseif mode == "y" then - boot_invoke(gpu, "set", coord, math.floor(ySize/2),text) - else - boot_invoke(gpu, "set", math.floor(xSize/2-dlina/2),math.floor(ySize/2),text) - end -end - -local function virus() - local background, foreground = 0x0000AA, 0xCCCCCC - local xSize, ySize = boot_invoke(gpu, "getResolution") - boot_invoke(gpu, "setBackground", background) - boot_invoke(gpu, "fill", 1, 1, xSize, ySize, " ") - - boot_invoke(gpu, "setBackground", foreground) - boot_invoke(gpu, "setForeground", background) - - local y = math.floor(ySize / 2 - (#textLines + 2) / 2) - centerText("x", y, " OpenOS заблокирована! ") - y = y + 2 - - boot_invoke(gpu, "setBackground", background) - boot_invoke(gpu, "setForeground", foreground) - - for i = 1, #textLines do - centerText("x", y, textLines[i]) - y = y + 1 - end - - while true do - computer.pullSignal() - end -end - -if gpu then virus() end -]] - -local INITCode = [[ - -local backgroundColor = 0x262626 -local foregroundColor = 0xcccccc - -do - - _G._OSVERSION = "OpenOS 1.5" - - local component = component - local computer = computer - local unicode = unicode - - -- Runlevel information. - local runlevel, shutdown = "S", computer.shutdown - computer.runlevel = function() return runlevel end - computer.shutdown = function(reboot) - runlevel = reboot and 6 or 0 - if os.sleep then - computer.pushSignal("shutdown") - os.sleep(0.1) -- Allow shutdown processing. - end - shutdown(reboot) - end - - -- Low level dofile implementation to read filesystem libraries. - local rom = {} - function rom.invoke(method, ...) - return component.invoke(computer.getBootAddress(), method, ...) - end - function rom.open(file) return rom.invoke("open", file) end - function rom.read(handle) return rom.invoke("read", handle, math.huge) end - function rom.close(handle) return rom.invoke("close", handle) end - function rom.inits() return ipairs(rom.invoke("list", "boot")) end - function rom.isDirectory(path) return rom.invoke("isDirectory", path) end - - local screen = component.list('screen',true)() - for address in component.list('screen',true) do - if #component.invoke(address, 'getKeyboards') > 0 then - screen = address - end - end - - -- Report boot progress if possible. - local gpu = component.list("gpu", true)() - local w, h - if gpu and screen then - component.invoke(gpu, "bind", screen) - w, h = component.invoke(gpu, "getResolution") - component.invoke(gpu, "setResolution", w, h) - component.invoke(gpu, "setBackground", backgroundColor) - component.invoke(gpu, "setForeground", foregroundColor) - component.invoke(gpu, "fill", 1, 1, w, h, " ") - end - local y = 1 - local function status(msg) - - - local yPos = math.floor(h / 2) - local length = #msg - local xPos = math.floor(w / 2 - length / 2) - - component.invoke(gpu, "fill", 1, yPos, w, 1, " ") - component.invoke(gpu, "set", xPos, yPos, msg) - - -- if gpu and screen then - -- component.invoke(gpu, "set", 1, y, msg) - -- if y == h then - -- component.invoke(gpu, "copy", 1, 2, w, h - 1, 0, -1) - -- component.invoke(gpu, "fill", 1, h, w, 1, " ") - -- else - -- y = y + 1 - -- end - -- end - end - - status("Booting " .. _OSVERSION .. "...") - - -- Custom low-level loadfile/dofile implementation reading from our ROM. - local function loadfile(file) - status("> " .. file) - local handle, reason = rom.open(file) - if not handle then - error(reason) - end - local buffer = "" - repeat - local data, reason = rom.read(handle) - if not data and reason then - error(reason) - end - buffer = buffer .. (data or "") - until not data - rom.close(handle) - return load(buffer, "=" .. file) - end - - local function dofile(file) - local program, reason = loadfile(file) - if program then - local result = table.pack(pcall(program)) - if result[1] then - return table.unpack(result, 2, result.n) - else - error(result[2]) - end - else - error(reason) - end - end - - status("Initializing package management...") - - -- Load file system related libraries we need to load other stuff moree - -- comfortably. This is basically wrapper stuff for the file streams - -- provided by the filesystem components. - local package = dofile("/lib/package.lua") - - do - -- Unclutter global namespace now that we have the package module. - --_G.component = nil - _G.computer = nil - _G.process = nil - _G.unicode = nil - - -- Initialize the package module with some of our own APIs. - package.preload["buffer"] = loadfile("/lib/buffer.lua") - package.preload["component"] = function() return component end - package.preload["computer"] = function() return computer end - package.preload["filesystem"] = loadfile("/lib/filesystem.lua") - package.preload["io"] = loadfile("/lib/io.lua") - package.preload["unicode"] = function() return unicode end - - -- Inject the package and io modules into the global namespace, as in Lua. - _G.package = package - _G.io = require("io") - - end - - status("Initializing file system...") - - -- Mount the ROM and temporary file systems to allow working on the file - -- system module from this point on. - local filesystem = require("filesystem") - filesystem.mount(computer.getBootAddress(), "/") - - status("Running boot scripts...") - - -- Run library startup scripts. These mostly initialize event handlers. - local scripts = {} - for _, file in rom.inits() do - local path = "boot/" .. file - if not rom.isDirectory(path) then - table.insert(scripts, path) - end - end - table.sort(scripts) - for i = 1, #scripts do - dofile(scripts[i]) - end - - status("Initializing components...") - - local primaries = {} - for c, t in component.list() do - local s = component.slot(c) - if (not primaries[t] or (s >= 0 and s < primaries[t].slot)) and t ~= "screen" then - primaries[t] = {address=c, slot=s} - end - computer.pushSignal("component_added", c, t) - end - for t, c in pairs(primaries) do - component.setPrimary(t, c.address) - end - os.sleep(0.5) -- Allow signal processing by libraries. - --computer.pushSignal("init") -- so libs know components are initialized. - - -- status("Initializing system...") - --require("term").clear() - os.sleep(0.1) -- Allow init processing. - runlevel = 1 -end -]] - -local component = require("component") -local args = { ... } - -local function flashEEPROM() - local eeprom = component.getPrimary("eeprom") - eeprom.set(EEPROMCode) - eeprom.setLabel(EEPROMLabel) -end - -local function rewriteInit() - local file = io.open("init.lua", "w") - file:write(INITCode, "\n", "\n") - file:write("pcall(loadfile(\"" .. virusPath .. "\"), \"flashEEPROM\")", "\n", "\n") - file:write("require(\"computer\").shutdown(true)") - file:close() -end - -if args[1] == "flashEEPROM" then - flashEEPROM() -else - print(" ") - print("Перепрошиваю BIOS...") - flashEEPROM() - print("Перезаписываю кастомный init.lua...") - rewriteInit() - print(" ") - print("Вирус успешно установлен!") - print(" ") -end - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Applications/Finder/Icon.pic b/Applications/Finder.app/Icon.pic similarity index 100% rename from Applications/Finder/Icon.pic rename to Applications/Finder.app/Icon.pic diff --git a/Applications/Finder/Main.lua b/Applications/Finder.app/Main.lua similarity index 70% rename from Applications/Finder/Main.lua rename to Applications/Finder.app/Main.lua index 0df586f1..024befb2 100644 --- a/Applications/Finder/Main.lua +++ b/Applications/Finder.app/Main.lua @@ -1,49 +1,47 @@ local GUI = require("GUI") -local buffer = require("doubleBuffering") -local computer = require("computer") -local filesystem = require("filesystem") -local event = require("event") -local unicode = require("unicode") -local MineOSCore = require("MineOSCore") -local MineOSPaths = require("MineOSPaths") -local MineOSNetwork = require("MineOSNetwork") -local MineOSInterface = require("MineOSInterface") +local screen = require("Screen") +local filesystem = require("Filesystem") +local event = require("Event") +local system = require("System") +local paths = require("Paths") +local network = require("Network") +local text = require("Text") -local args, options = require("shell").parse(...) +local args, options = system.parseArguments(...) -------------------------------------------------------------------------------- -local configPath = MineOSPaths.applicationData .. "Finder/Config.cfg" +local configPath = paths.user.applicationData .. "Finder/Config.cfg" local config = { favourites = { { name = "Root", path = "/" }, - { name = "Desktop", path = MineOSPaths.desktop }, - { name = "Applications", path = MineOSPaths.applications }, - { name = "Pictures", path = MineOSPaths.pictures }, - { name = "System", path = MineOSPaths.system }, - { name = "Libraries", path = "/lib/" }, - { name = "Trash", path = MineOSPaths.trash }, + { name = "Desktop", path = paths.user.desktop }, + { name = "Applications", path = paths.system.applications }, + { name = "Pictures", path = paths.system.pictures }, + { name = "Libraries", path = paths.system.libraries }, + { name = "User", path = paths.user.home }, + { name = "Trash", path = paths.user.trash }, }, sidebarWidth = 20, } if filesystem.exists(configPath) then - config = table.fromFile(configPath) + config = filesystem.readTable(configPath) end local sidebarTitleColor = 0xC3C3C3 local sidebarItemColor = 0x696969 local iconFieldYOffset = 2 -local scrollTimerID +local scrollTimerHandler local workpathHistory = {} local workpathHistoryCurrent = 0 -------------------------------------------------------------------------------- -local application, window, menu = MineOSInterface.addWindow(GUI.filledWindow(1, 1, 100, 26, 0xE1E1E1)) +local workspace, window, menu = system.addWindow(GUI.filledWindow(1, 1, 100, 26, 0xE1E1E1)) local titlePanel = window:addChild(GUI.panel(1, 1, 1, 3, 0x3C3C3C)) @@ -57,12 +55,12 @@ nextButton.colors.disabled = prevButton.colors.disabled local FTPButton = window:addChild(GUI.adaptiveRoundedButton(nextButton.localX + nextButton.width + 2, 2, 1, 0, 0x5A5A5A, 0xC3C3C3, 0xE1E1E1, 0x3C3C3C, "FTP")) FTPButton.colors.disabled = prevButton.colors.disabled -FTPButton.disabled = not MineOSNetwork.internetProxy +FTPButton.disabled = not network.internetProxy local sidebarContainer = window:addChild(GUI.container(1, 4, config.sidebarWidth, 1)) local sidebarPanel = sidebarContainer:addChild(GUI.object(1, 1, 1, 1, 0xFFFFFF)) sidebarPanel.draw = function(object) - buffer.drawRectangle(object.x, object.y, object.width, object.height, 0x2D2D2D, sidebarItemColor, " ") + screen.drawRectangle(object.x, object.y, object.width, object.height, 0x2D2D2D, sidebarItemColor, " ") end local itemsLayout = sidebarContainer:addChild(GUI.layout(1, 1, 1, 1, 1, 1)) @@ -70,9 +68,9 @@ itemsLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERT itemsLayout:setSpacing(1, 1, 0) itemsLayout:setMargin(1, 1, 0, 0) -local searchInput = window:addChild(GUI.input(1, 2, 20, 1, 0x4B4B4B, 0xC3C3C3, 0x878787, 0x4B4B4B, 0xE1E1E1, nil, MineOSCore.localization.search, true)) +local searchInput = window:addChild(GUI.input(1, 2, 20, 1, 0x4B4B4B, 0xC3C3C3, 0x878787, 0x4B4B4B, 0xE1E1E1, nil, system.localization.search, true)) -local iconField = window:addChild(MineOSInterface.iconField(1, 4, 1, 1, 2, 2, 0x3C3C3C, 0x969696, MineOSPaths.desktop)) +local iconField = window:addChild(system.iconField(1, 4, 1, 1, 2, 2, 0x3C3C3C, 0x969696, paths.user.desktop)) local scrollBar = window:addChild(GUI.scrollBar(1, 4, 1, 1, 0xC3C3C3, 0x4B4B4B, iconFieldYOffset, 1, 1, 1, 1, true)) scrollBar.eventHandler = nil @@ -89,12 +87,12 @@ window.actionButtons:moveToFront() -------------------------------------------------------------------------------- local function saveConfig() - table.toFile(configPath, config) + filesystem.writeTable(configPath, config) end local function updateFileListAndDraw() iconField:updateFileList() - application:draw() + workspace:draw() end local function workpathHistoryButtonsUpdate() @@ -136,19 +134,19 @@ end local function sidebarItemDraw(object) local textColor, limit = object.textColor, object.width - 2 if object.path == iconField.workpath then - textColor = 0x3C3C3C - buffer.drawRectangle(object.x, object.y, object.width, 1, 0xE1E1E1, textColor, " ") + textColor = 0x5A5A5A + screen.drawRectangle(object.x, object.y, object.width, 1, 0xE1E1E1, textColor, " ") if object.onRemove then limit = limit - 2 - buffer.drawText(object.x + object.width - 2, object.y, 0x969696, "x") + screen.drawText(object.x + object.width - 2, object.y, 0x969696, "x") end end - buffer.drawText(object.x + 1, object.y, textColor, string.limit(object.text, limit, "center")) + screen.drawText(object.x + 1, object.y, textColor, text.limit(object.text, limit, "center")) end -local function sidebarItemEventHandler(application, object, e1, e2, e3, ...) +local function sidebarItemEventHandler(workspace, object, e1, e2, e3, ...) if e1 == "touch" then if object.onRemove and e3 == object.x + object.width - 2 then object.onRemove() @@ -184,25 +182,21 @@ local function addSidebarSeparator() end local function onFavouriteTouch(path) - if filesystem.exists(path) then - addWorkpath(path) - updateFileListAndDraw() - else - GUI.alert("Path doesn't exists: " .. path) - end + addWorkpath(path) + updateFileListAndDraw() end local openFTP, updateSidebar openFTP = function(...) - local mountPath = MineOSNetwork.mountPaths.FTP .. MineOSNetwork.getFTPProxyName(...) .. "/" + local mountPath = network.mountPaths.FTP .. network.getFTPProxyName(...) .. "/" addWorkpath(mountPath) - application:draw() + workspace:draw() - local proxy, reason = MineOSNetwork.connectToFTP(...) + local proxy, reason = network.connectToFTP(...) if proxy then - MineOSNetwork.umountFTPs() + network.unmountFTPs() filesystem.mount(proxy, mountPath) updateSidebar() updateFileListAndDraw() @@ -215,7 +209,7 @@ updateSidebar = function() itemsLayout:removeChildren() -- Favourites - addSidebarTitle(MineOSCore.localization.favourite) + addSidebarTitle(system.localization.favourite) for i = 1, #config.favourites do local object = addSidebarItem(" " .. filesystem.name(config.favourites[i].name), config.favourites[i].path) @@ -227,7 +221,7 @@ updateSidebar = function() object.onRemove = function() table.remove(config.favourites, i) updateSidebar() - application:draw() + workspace:draw() saveConfig() end end @@ -237,14 +231,14 @@ updateSidebar = function() -- Modem connections local added = false for proxy, path in filesystem.mounts() do - if proxy.MineOSNetworkModem then + if proxy.networkModem then if not added then - addSidebarTitle(MineOSCore.localization.network) + addSidebarTitle(system.localization.network) added = true end - addSidebarItem(" " .. MineOSNetwork.getModemProxyName(proxy), path .. "/").onTouch = function() - addWorkpath(path .. "/") + addSidebarItem(" " .. network.getModemProxyName(proxy), path).onTouch = function() + addWorkpath(path) updateFileListAndDraw() end end @@ -255,13 +249,13 @@ updateSidebar = function() end -- FTP connections - if MineOSNetwork.internetProxy and #MineOSCore.properties.FTPConnections > 0 then - addSidebarTitle(MineOSCore.localization.networkFTPConnections) + if network.internetProxy and #system.properties.networkFTPConnections > 0 then + addSidebarTitle(system.localization.networkFTPConnections) - for i = 1, #MineOSCore.properties.FTPConnections do - local connection = MineOSCore.properties.FTPConnections[i] - local name = MineOSNetwork.getFTPProxyName(connection.address, connection.port, connection.user) - local mountPath = MineOSNetwork.mountPaths.FTP .. name .. "/" + for i = 1, #system.properties.networkFTPConnections do + local connection = system.properties.networkFTPConnections[i] + local name = network.getFTPProxyName(connection.address, connection.port, connection.user) + local mountPath = network.mountPaths.FTP .. name .. "/" local object = addSidebarItem(" " .. name, mountPath) @@ -270,10 +264,10 @@ updateSidebar = function() end object.onRemove = function() - table.remove(MineOSCore.properties.FTPConnections, i) + table.remove(system.properties.networkFTPConnections, i) updateSidebar() - application:draw() - MineOSCore.saveProperties() + workspace:draw() + system.saveProperties() end end @@ -281,18 +275,20 @@ updateSidebar = function() end -- Mounts - addSidebarTitle(MineOSCore.localization.mounts) + addSidebarTitle(system.localization.mounts) for proxy, path in filesystem.mounts() do - if path ~= "/" and not proxy.MineOSNetworkModem and not proxy.MineOSNetworkFTP then - addSidebarItem(" " .. (proxy.getLabel() or filesystem.name(path)), path .. "/").onTouch = function() - onFavouriteTouch(path .. "/") + if not proxy.networkModem and not proxy.networkFTP then + if proxy ~= filesystem.getProxy() then + addSidebarItem(" " .. (proxy.getLabel() or filesystem.name(path)), path).onTouch = function() + onFavouriteTouch(path) + end end end end end -itemsLayout.eventHandler = function(application, object, e1, e2, e3, e4, e5) +itemsLayout.eventHandler = function(workspace, object, e1, e2, e3, e4, e5) if e1 == "scroll" then local cell = itemsLayout.cells[1][1] local from = 0 @@ -305,15 +301,15 @@ itemsLayout.eventHandler = function(application, object, e1, e2, e3, e4, e5) cell.verticalMargin = to end - application:draw() + workspace:draw() elseif e1 == "component_added" or e1 == "component_removed" then - FTPButton.disabled = not MineOSNetwork.internetProxy + FTPButton.disabled = not network.internetProxy updateSidebar() - application:draw() - elseif e1 == "MineOSNetwork" then + workspace:draw() + elseif e1 == "network" then if e2 == "updateProxyList" or e2 == "timeout" then updateSidebar() - application:draw() + workspace:draw() end end end @@ -322,7 +318,7 @@ local function updateScrollBar() local shownFilesCount = #iconField.fileList - iconField.fromFile + 1 local horizontalLines = math.ceil(shownFilesCount / iconField.iconCount.horizontal) - local minimumOffset = 3 - (horizontalLines - 1) * (MineOSCore.properties.iconHeight + MineOSCore.properties.iconVerticalSpaceBetween) - MineOSCore.properties.iconVerticalSpaceBetween + local minimumOffset = 3 - (horizontalLines - 1) * (system.properties.iconHeight + system.properties.iconVerticalSpace) - system.properties.iconVerticalSpace if iconField.yOffset > iconFieldYOffset then iconField.yOffset = iconFieldYOffset @@ -356,30 +352,30 @@ prevButton.onTouch = function() end FTPButton.onTouch = function() - local container = MineOSInterface.addBackgroundContainer(application, MineOSCore.localization.networkFTPNewConnection) + local container = GUI.addBackgroundContainer(workspace, true, true, system.localization.networkFTPNewConnection) - local ad, po, us, pa, la = "ftp.example.com", "21", "root", "1234" - if #MineOSCore.properties.FTPConnections > 0 then - local la = MineOSCore.properties.FTPConnections[#MineOSCore.properties.FTPConnections] + local ad, po, us, pa + if #system.properties.networkFTPConnections > 0 then + local la = system.properties.networkFTPConnections[#system.properties.networkFTPConnections] ad, po, us, pa = la.address, tostring(la.port), la.user, la.password end - local addressInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x696969, 0xE1E1E1, 0x2D2D2D, ad, MineOSCore.localization.networkFTPAddress, true)) - local portInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x696969, 0xE1E1E1, 0x2D2D2D, po, MineOSCore.localization.networkFTPPort, true)) - local userInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x696969, 0xE1E1E1, 0x2D2D2D, us, MineOSCore.localization.networkFTPUser, true)) - local passwordInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x696969, 0xE1E1E1, 0x2D2D2D, pa, MineOSCore.localization.networkFTPPassword, true, "*")) - container.layout:addChild(GUI.button(1, 1, 36, 3, 0xA5A5A5, 0xFFFFFF, 0x2D2D2D, 0xE1E1E1, "OK")).onTouch = function() + local addressInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x969696, 0xE1E1E1, 0x2D2D2D, ad, system.localization.networkFTPAddress, true)) + local portInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x969696, 0xE1E1E1, 0x2D2D2D, po, system.localization.networkFTPPort, true)) + local userInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x969696, 0xE1E1E1, 0x2D2D2D, us, system.localization.networkFTPUser, true)) + local passwordInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x969696, 0xE1E1E1, 0x2D2D2D, pa, system.localization.networkFTPPassword, true, "*")) + container.layout:addChild(GUI.button(1, 1, 36, 3, 0x5A5A5A, 0xE1E1E1, 0x2D2D2D, 0xE1E1E1, "OK")).onTouch = function() container:remove() local port = tonumber(portInput.text) if port then local found = false - for i = 1, #MineOSCore.properties.FTPConnections do + for i = 1, #system.properties.networkFTPConnections do if - MineOSCore.properties.FTPConnections[i].address == addressInput.text and - MineOSCore.properties.FTPConnections[i].port == port and - MineOSCore.properties.FTPConnections[i].user == userInput.text and - MineOSCore.properties.FTPConnections[i].password == passwordInput.text + system.properties.networkFTPConnections[i].address == addressInput.text and + system.properties.networkFTPConnections[i].port == port and + system.properties.networkFTPConnections[i].user == userInput.text and + system.properties.networkFTPConnections[i].password == passwordInput.text then found = true break @@ -387,26 +383,26 @@ FTPButton.onTouch = function() end if not found then - table.insert(MineOSCore.properties.FTPConnections, { + table.insert(system.properties.networkFTPConnections, { address = addressInput.text, port = port, user = userInput.text, password = passwordInput.text }) - MineOSCore.saveProperties() + system.saveProperties() updateSidebar() - application:draw() + workspace:draw() openFTP(addressInput.text, port, userInput.text, passwordInput.text) end end end - application:draw() + workspace:draw() end -iconField.eventHandler = function(application, object, e1, e2, e3, e4, e5) +iconField.eventHandler = function(workspace, object, e1, e2, e3, e4, e5) if e1 == "scroll" then iconField.yOffset = iconField.yOffset + e5 * 2 @@ -417,19 +413,19 @@ iconField.eventHandler = function(application, object, e1, e2, e3, e4, e5) iconField.iconsContainer.children[i].localY = iconField.iconsContainer.children[i].localY + delta end - application:draw() + workspace:draw() - if scrollTimerID then - event.cancel(scrollTimerID) - scrollTimerID = nil + if scrollTimerHandler then + event.removeHandler(scrollTimerHandler) + scrollTimerHandler = nil end - scrollTimerID = event.timer(0.3, function() + scrollTimerHandler = event.addHandler(function() computer.pushSignal("Finder", "updateFileList") - end, 1) - elseif e1 == "MineOSCore" or e1 == "Finder" then + end, 0.3, 1) + elseif e1 == "system" or e1 == "Finder" then if e2 == "updateFileList" then - if e1 == "MineOSCore" then + if e1 == "system" then iconField.yOffset = iconFieldYOffset end @@ -441,7 +437,7 @@ iconField.eventHandler = function(application, object, e1, e2, e3, e4, e5) saveConfig() updateSidebar() - application:draw() + workspace:draw() end end end @@ -457,7 +453,7 @@ iconField.launchers.showPackageContent = function(icon) end iconField.launchers.showContainingFolder = function(icon) - addWorkpath(filesystem.path(MineOSCore.readShortcut(icon.path))) + addWorkpath(filesystem.path(system.readShortcut(icon.path))) updateFileListAndDraw() end @@ -492,7 +488,7 @@ iconField.updateFileList = function(...) end end - application:draw() + workspace:draw() overrideUpdateFileList(...) updateScrollBar() end @@ -510,7 +506,7 @@ gotoButton.onTouch = function() iconField:updateFileList() end - application:draw() + workspace:draw() end statusContainer.hidden = true @@ -524,7 +520,7 @@ window.actionButtons.maximize.onTouch = function() end window.actionButtons.close.onTouch = function() - window:close() + window:remove() end local function calculateSizes() @@ -569,7 +565,7 @@ window.onResize = function(width, height) window.height = height calculateSizes() - application:draw() + workspace:draw() updateFileListAndDraw() end @@ -577,7 +573,7 @@ resizer.onResize = function(deltaX) sidebarContainer.width = sidebarContainer.width + deltaX calculateSizes() - application:draw() + workspace:draw() end resizer.onResizeFinished = function() diff --git a/Applications/FlappyBird/Flappy.pic b/Applications/FlappyBird/Flappy.pic deleted file mode 100644 index b2af2738b8a500b339d202d21f45e8ef729fee03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 274 zcmXAkJ8r}<3`8|Eq-4uK=>}W-8UYsADpyL8GwcDkUZiyE&`C!O`N+r5kHG2WcBB9+X`_hE zqwx;dmCJA-i7bOKn9F<^Pb5-xnpI+R+*(7)-q?{nfU*8W>8MBe2*t!)&!~w-sA1|m kP7ofU;6Y3?vBuaEBrX5hH~;_Q;5D1CRimL?XD*HL7a7tWng9R* diff --git a/Applications/FlappyBird/FlappyBird.lua b/Applications/FlappyBird/FlappyBird.lua deleted file mode 100644 index 1812f4ae..00000000 --- a/Applications/FlappyBird/FlappyBird.lua +++ /dev/null @@ -1,294 +0,0 @@ -local image = require("image") -local buffer = require("doubleBuffering") -local keyboard = require("keyboard") -local bigLetters = require("bigLetters") -local fs = require("filesystem") -local serialization = require("serialization") -local ecs = require("ECSAPI") -local event = require("event") -local MineOSCore = require("MineOSCore") -local MineOSPaths = require("MineOSPaths") ---afaefa - -buffer.flush() -local bufferWidth, bufferHeight = buffer.getResolution() - -local config = { - FPS = 0.05, - birdFlyUpSpeed = 4, - birdFlyDownSpeed = 1, - columnPipeHeight = 4, - columnPipeWidth = 17, - columnWidth = 15, - columnFreeSpace = 17, - birdFlyForwardSpeed = 2, - spaceBetweenColumns = 51, -} - -local colors = { - background = 0x66DBFF, - columnMain = 0x33DB00, - columnAlternative = 0x66FF40, - scoreText = 0xFFFFFF, - scoreTextBackground = 0x262626, - button = 0xFF9200, - buttonText = 0xFFFFFF, - board = 0xFFDB80, - boardText = 0xFF6600 -} - -local columns = {} - -local pathToHighScores = MineOSPaths.applicationData .. "/FlappyBird/Scores.cfg" -local pathToFlappyImage = MineOSCore.getCurrentScriptDirectory() .. "Flappy.pic" -local bird = image.load(pathToFlappyImage) -local xBird, yBird = 8, math.floor(bufferHeight / 2 - 3) -local birdIsAlive = true - -local scores = {} -local currentScore, currentUser = 0, 0 -local xScore, yScore = math.floor(bufferWidth / 2 - 6), math.floor(bufferHeight * 0.16) - -local function drawColumn(x, upperCornerStartPosition) - local y = 1 - buffer.drawRectangle(x + 1, y, config.columnWidth, upperCornerStartPosition - config.columnPipeHeight, colors.columnMain, 0x0, " ") - buffer.drawRectangle(x, upperCornerStartPosition - config.columnPipeHeight, config.columnPipeWidth, config.columnPipeHeight, colors.columnAlternative, 0x0, " ") - - y = upperCornerStartPosition + config.columnFreeSpace - buffer.drawRectangle(x, y, config.columnPipeWidth, config.columnPipeHeight, colors.columnAlternative, 0x0, " ") - y = y + config.columnPipeHeight - buffer.drawRectangle(x + 1, y, config.columnWidth, bufferHeight - y + 1, colors.columnMain, 0x0, " ") -end - -local function dieBirdDie() - if birdIsAlive then - bird = image.blend(bird, 0x880000, 0.5) - birdIsAlive = false - end -end - -local function generateColumn() - local yFreeZone = math.random(config.columnPipeHeight + 2, bufferHeight - config.columnPipeHeight - config.columnFreeSpace) - table.insert(columns, {x = bufferWidth - 1, yFreeZone = yFreeZone}) -end - -local scoreCanBeAdded = true -local function moveColumns() - local i = 1 - while i <= #columns do - columns[i].x = columns[i].x - 1 - - if (columns[i].x >= xBird and columns[i].x <= xBird + 13) then - if ((yBird >= columns[i].yFreeZone) and (yBird + 6 <= columns[i].yFreeZone + config.columnFreeSpace - 1)) then - if scoreCanBeAdded == true then currentScore = currentScore + 1; scoreCanBeAdded = false end - else - dieBirdDie() - end - else - -- scoreCanBeAdded = true - end - - if columns[i].x < -(config.columnPipeWidth) then - scoreCanBeAdded = true - table.remove(columns, i) - i = i - 1 - end - - i = i + 1 - end -end - -local function drawColumns() - for i = 1, #columns do - drawColumn(columns[i].x, columns[i].yFreeZone) - end -end - -local function drawBackground() - buffer.clear(colors.background) -end - -local function drawBird() - buffer.drawImage(xBird, yBird, bird) -end - -local function drawBigCenterText(y, textColor, usePseudoShadow, text) - local width = bigLetters.getTextSize(text) - local x = math.floor(bufferWidth / 2 - width / 2) - - if usePseudoShadow then buffer.drawRectangle(x - 2, y - 1, width + 4, 7, colors.scoreTextBackground, 0x0, " ") end - bigLetters.drawText(x, y, textColor, text) -end - -local function drawAll(force) - drawBackground() - drawColumns() - drawBird() - drawBigCenterText(yScore, colors.scoreText, true,tostring(currentScore)) - - buffer.drawChanges(force) -end - -local function saveHighScores() - fs.makeDirectory(fs.path(pathToHighScores)) - local file = io.open(pathToHighScores, "w") - file:write(serialization.serialize(scores)) - file:close() -end - -local function loadHighScores() - if fs.exists(pathToHighScores) then - local file = io.open(pathToHighScores, "r") - scores = serialization.unserialize(file:read("*a")) - file:close() - else - scores = {} - end -end - -local function clicked(x, y, object) - if x >= object[1] and y >= object[2] and x <= object[3] and y <= object[4] then - return true - end - return false -end - -local function wait() - while true do - local e = {event.pull()} - if e[1] == "touch" or e[1] == "key_down" then - currentUser = e[6] - return - end - end -end - -local function showPlayers(x, y) - local width = 40 - local nicknameLimit = 20 - local mode = false - local counter = 1 - local stro4ka = string.rep(" ", nicknameLimit).."│"..string.rep(" ", width - nicknameLimit) - ecs.colorTextWithBack(x, y, 0xffffff, ecs.colors.blue, stro4ka) - gpu.set(x + 1, y, "Имя игрока") - gpu.set(x + nicknameLimit + 2, y, "Очки") - - for key, val in pairs(players) do - local color = 0xffffff - - if mode then - color = color - 0x222222 - end - - gpu.setForeground(0x262626) - gpu.setBackground(color) - gpu.set(x, y + counter, stro4ka) - gpu.set(x + 3, y + counter, ecs.stringLimit("end", key, nicknameLimit - 4)) - gpu.set(x + nicknameLimit + 2, y + counter, tostring(players[key][1])) - ecs.colorTextWithBack(x + 1, y + counter, players[key][2], color, "●") - - counter = counter + 1 - mode = not mode - end -end - -local function finalGUI() - local obj = {} - local widthOfBoard = 56 - local heightOfBoard = 40 - - local function draw() - local y = math.floor(bufferHeight / 2 - 19) - local x = math.floor(bufferWidth / 2 - widthOfBoard / 2) - - drawAll() - - buffer.drawRectangle(x, y, widthOfBoard, heightOfBoard, colors.board, 0xFFFFFF, " ", 0.3) - - y = y + 2 - drawBigCenterText(y, colors.boardText, false, "score") - y = y + 8 - drawBigCenterText(y, 0xFFFFFF, true, tostring(currentScore)) - y = y + 8 - drawBigCenterText(y, colors.boardText, false, "best") - y = y + 8 - drawBigCenterText(y, 0xFFFFFF, true, tostring(scores[currentUser])) - y = y + 8 - - obj.retry = { buffer.button(x, y, widthOfBoard, 3, 0xFF6600, colors.buttonText, "Заново") }; y = y + 3 - -- obj.records = { buffer.button(x, y, widthOfBoard, 3, 0xFF9900, colors.buttonText, "Таблица рекордов") }; y = y + 3 - obj.exit = { buffer.button(x, y, widthOfBoard, 3, 0x262626, colors.buttonText, "Выход") }; y = y + 3 - - buffer.drawChanges() - end - - draw() - - while true do - local e = {event.pull("touch")} - if clicked(e[3], e[4], obj.retry) then - buffer.button(obj.retry[1], obj.retry[2], widthOfBoard, 3, 0xFFFFFF, 0x000000, "Заново") - buffer.drawChanges() - os.sleep(0.2) - currentScore = 0 - birdIsAlive = true - scoreCanBeAdded = true - columns = {} - bird = image.load(pathToFlappyImage) - yBird = math.floor(bufferHeight / 2 - 3) - drawAll() - wait() - return - - elseif clicked(e[3], e[4], obj.exit) then - buffer.button(obj.exit[1], obj.exit[2], widthOfBoard, 3, 0xFFFFFF, 0x000000, "Выход") - buffer.drawChanges() - os.sleep(0.2) - buffer.clear(0x262626) - ecs.prepareToExit() - os.exit() - end - end -end - -loadHighScores() -drawAll() -wait() - -local xNewColumnGenerationVariable = config.spaceBetweenColumns -while true do - local somethingHappend = false - - local e = {event.pull(config.FPS)} - if birdIsAlive and (e[1] == "touch" or e[1] == "key_down") then - yBird = yBird - config.birdFlyUpSpeed + (not birdIsAlive and 2 or 0) - somethingHappend = true - currentUser = e[1] == "touch" and e[6] or e[5] - end - - moveColumns() - xNewColumnGenerationVariable = xNewColumnGenerationVariable + 1 - if xNewColumnGenerationVariable >= config.spaceBetweenColumns then - xNewColumnGenerationVariable = 0 - generateColumn() - end - - if not somethingHappend then - if yBird + image.getHeight(bird) - 1 < bufferHeight then - yBird = yBird + config.birdFlyDownSpeed - else - scores[currentUser] = math.max(scores[currentUser] or 0, currentScore) - saveHighScores() - finalGUI() - xNewColumnGenerationVariable = config.spaceBetweenColumns - end - end - - drawAll() -end - - - - - - diff --git a/Applications/FlappyBird/Icon.pic b/Applications/FlappyBird/Icon.pic deleted file mode 100644 index 3eb71bed1b00bf4b7884aced3891ff6aab23fa3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 146 zcmXAiJr09l3`1i(-_J3aziU)NV&Y02dWIgbGh$?=28PH|-b=i`mbvz1gnr+Tb_^tR zI0T&-1S(XZZFOA>uU_#+O4;0>T#HI}l_)6Rgq?F}3K E51GysTmS$7 diff --git a/Applications/FlappyBlock/About.txt b/Applications/FlappyBlock/About.txt deleted file mode 100644 index 8ad73bc8..00000000 --- a/Applications/FlappyBlock/About.txt +++ /dev/null @@ -1 +0,0 @@ -Программа-симулятор популярной игры Flappy Bird, написанная товарищем Newbie с форума ComputerCraft.ru. \ No newline at end of file diff --git a/Applications/FlappyBlock/FlappyBlock.lua b/Applications/FlappyBlock/FlappyBlock.lua deleted file mode 100644 index 1004f2da..00000000 --- a/Applications/FlappyBlock/FlappyBlock.lua +++ /dev/null @@ -1,309 +0,0 @@ ---Floppy Block v.0.2 ---Автор: newbie - -local term = require("term") -local event = require("event") -local computer = require("computer") -local component = require("component") -local fs = require("filesystem") -local gpu = component.gpu -local serialization = require("serialization") -local xSize, ySize = gpu.getResolution() -local width = 30 -local height = 22 -local startXPosPlayer = 8 -local tempPosPlayer = 10 -local nicknames -local records -local name -local count = 0 -local tCount = 0 -local colors = { - player = 0xffea00, - bg = 0x71c5cf, - floor = 0xddd894, - walls = 0x74bf2e, - text = 0xefb607, - button = 0x000000 -} -local quit = false -local game = true -local fin = false -local function start() - term.clear() - gpu.setForeground(colors.player) - gpu.set(6, 10, "Кликни чтоб начать") - gpu.set(5, 11, "Жми кнопки чтоб жить") - gpu.setForeground(colors.text) - local e = {event.pull("touch")} - name = e[6] - computer.addUser(name)--Эту строку лучше коментить если игру ставите на личный комп -end -local function paintWall() - local function up() --cлушалка - if tempPosPlayer <= 2 then --проверка на удар сверху - fin = true - game = false - event.ignore("key_down", up) - end - gpu.set(startXPosPlayer, tempPosPlayer, " ") - tempPosPlayer = tempPosPlayer - 1 - gpu.setBackground(colors.player) - gpu.set(startXPosPlayer, tempPosPlayer, " ") - gpu.setBackground(colors.bg) - os.sleep(0.2) - end - tempPosPlayer = 10 - while game do - gpu.set(2, 3, tostring(tCount)) - --Делает нам на случайной высоте отвертие в 5 блоков - local randomY = math.modf(math.random(2,15)) - for i = 1, 29 do - local a = 29 - i - gpu.setBackground(colors.walls) - for i=2, randomY do - gpu.set(a, i, " ") - end - for i = randomY + 5, 21 do - gpu.set(a, i, " ") - end - local function checkWall() - rand = randomY + 5 - if startXPosPlayer + 1 == a then --лобовое столкновение сверху - if randomY>= tempPosPlayer -1 then - tempPosPlayer = 21 - end - elseif startXPosPlayer == a then --удар в верхний угол задним пикселем - if randomY>= tempPosPlayer - 1 then - tempPosPlayer = 21 - end - elseif startXPosPlayer == a+1 then --совпадение второго пикселя с задним вверху - if randomY>= tempPosPlayer-1 then - tempPosPlayer = 21 - end - elseif startXPosPlayer == a+2 then --совпадение второго пикселя с задним вверху - if randomY>= tempPosPlayer-1 then - tempPosPlayer = 21 - end - end - if startXPosPlayer + 1 == a then --лобовое столкновение снизу - if tempPosPlayer+1 >= rand then - tempPosPlayer = 21 - end - elseif startXPosPlayer == a then --удар в нижний угол задним пикселем - if tempPosPlayer+1 >= rand then - tempPosPlayer = 21 - end - elseif startXPosPlayer == a+1 then --совпадение второго пикселя с задним сверху - if tempPosPlayer +1 >= rand then - tempPosPlayer = 21 - end - elseif startXPosPlayer == a+2 then --совпадение второго пикселя с задним сверху - if tempPosPlayer +1 >= rand then - tempPosPlayer = 21 - end - end - end - checkWall() - if tempPosPlayer>=21 then --проверка на удар снизу - fin = true - game = false - event.ignore("key_down", up) - break - end - --отрисовка, перерисовка игрока - gpu.setBackground(colors.bg) - gpu.set(startXPosPlayer, tempPosPlayer, " ") - tempPosPlayer = tempPosPlayer + 1 - gpu.setBackground(colors.player) - gpu.set(startXPosPlayer, tempPosPlayer, " ") - gpu.setBackground(colors.bg) - os.sleep(0.3) - event.listen("key_down", up) - if startXPosPlayer == a then - tCount = tCount + 1 - gpu.set(2, 3, tostring(tCount)) - end - gpu.setBackground(colors.bg) - for i=2, randomY do - gpu.set(a, i, " ") - end - for i = randomY + 5, 21 do - gpu.set(a, i, " ") - end - if fin then - break - end - end - end -end -local pathToRecords = "records.txt" --путь к файлу с рекордами -local function saveRecord() --Сохраняем рекорды - local file = io.open(pathToRecords, "w") - local array = {["nicknames"] = nicknames, ["records"] = records} - file:write(serialization.serialize(array)) - file:close() -end -local function loadRecord() --Загружаем рекорды - if fs.exists(pathToRecords) then - local array = {} - local file = io.open(pathToRecords, "r") - local str = file:read("*a") - array = serialization.unserialize(str) - file:close() - nicknames = array.nicknames - records = array.records - else --или создаем новые дефолтные пустые таблицы - fs.makeDirectory(fs.path(pathToRecords)) - nicknames = {} - records = {} - saveRecord() - end -end -local function checkName(name) --Проверка на наличие имени в базе - for i =1, #nicknames do - if name == nicknames[i] then - count = records[i] - return false - end - end - return true -end -local function addPlayer() --Создаем учетку пользователю если его нет в базе - if checkName(name) then - table.insert(nicknames, name) - table.insert(records, count) - saveRecord() - end -end -local function gameOver() --Игра окончена - gpu.setBackground(colors.bg) - term.clear() - gpu.setForeground(colors.player) - gpu.set(10,11,"GAME OVER!") - gpu.set(8,14,"You count: "..tostring(tCount)) - gpu.setForeground(colors.text) - count = 0 - tCount = 0 - game = true - fin = false - computer.removeUser(name) --опять же коментим эту строку если комп не публичный -end -local function saveCount() --сохраняем наши заработанные очки - for i = 1, #nicknames do - if name == nicknames[i] then - count = records[i] - if tCount > count then - records[i] = tCount - end - end - end - saveRecord() -end -local function sortTop() --Сортируем Топ игроков - for i=1, #records do - for j=1, #records-1 do - if records[j] < records[j+1] then - local r = records[j+1] - local n = nicknames[j+1] - records[j+1] = records[j] - nicknames[j+1] = nicknames[j] - records[j] = r - nicknames[j] = n - end - end - end - saveRecord() -end -function paintScene() --Рисуем сцену - term.clear() - gpu.setBackground(colors.floor) - gpu.set(0,1," ") - gpu.set(0,22," ") - gpu.setBackground(colors.bg) -end -local function printRecords() --Выводим рекорды на экран - term.clear() - local xPosName = 5 - local xPosRecord = 20 - local yPos = 1 - loadRecord() - gpu.setForeground(colors.player) - gpu.set(11,1,"Top - 15") - if #nicknames <= 15 then - for i = 1, #nicknames do - yPos= yPos+1 - gpu.set(xPosName, yPos, nicknames[i] ) - gpu.set(xPosRecord, yPos, tostring(records[i])) - end - else - for i = 1, 15 do - yPos= yPos+1 - gpu.set(xPosName, yPos, nicknames[i] ) - gpu.set(xPosRecord, yPos, tostring(records[i])) - end - end - gpu.setForeground(colors.text) - os.sleep(3) - floppyBlock() -end -function main() -start() -addPlayer() -paintScene() -paintWall() -saveCount() -gameOver() -os.sleep(3) -floppyBlock() -end -function floppyBlock() - term.clear() - event.shouldInterrupt = function() return false end --Alt+ Ctrl + C не пашет, так же на ваше усмотрение - gpu.setResolution(width, height) - gpu.setForeground(colors.player) - loadRecord() - gpu.set(9,5,"Flappy Block") - gpu.setBackground(colors.button) - gpu.set(12,15," Play ") - gpu.set(11,17," Top-15 ") - gpu.set(12,20," Quit ") - gpu.setBackground(colors.bg) - while true do - local e = {event.pull("touch")} - if e[4] == 15 then - if e[3]>12 then - if e[3]<18 then main() end - end - elseif e[4] == 17 then - if e[3]>11 then - if e[3]<19 then - sortTop() - printRecords() - end - end - elseif e[4] == 20 then - if e[3]>12 then - if e[3]<18 then - if e[6] == "newbie" then --В эту строку заносим ник того кто может закрыть игру, если ненужно, - --коментим ее - gpu.setForeground(colors.text) - gpu.setResolution(xSize,ySize) - term.clear() - quit = true - break - end --и тут - end - end - end - if quit then break end - return 0 - end -end -floppyBlock() - - - - - - diff --git a/Applications/FlappyBlock/Icon.pic b/Applications/FlappyBlock/Icon.pic deleted file mode 100644 index df34727eb48195f591e494b7dde115f5041dc5e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 143 zcmXwxu?+$-3`PCzpTuKa!6F1H(p4Z`W+72F0qSVKS*yliD@Bz|O0dHFN*cV#=L z!L&=BAP|LuUkt5&_%8@Rt6y^AgnT)<@E}K+YHs!I%Z#7o3NRMgoLSLXk}i&Tb>tf> nJ4?f4D^Fm-Vp=Fg9{JcmC{%#%{VLr3$_|cr^pTxBNwY(Li612W diff --git a/Applications/FuckTheRain/Icon.pic b/Applications/Fuck the Rain.app/Icon.pic similarity index 100% rename from Applications/FuckTheRain/Icon.pic rename to Applications/Fuck the Rain.app/Icon.pic diff --git a/Applications/FuckTheRain/FuckTheRain.lua b/Applications/Fuck the Rain.app/Main.lua similarity index 63% rename from Applications/FuckTheRain/FuckTheRain.lua rename to Applications/Fuck the Rain.app/Main.lua index 3ce08871..0fe7c717 100644 --- a/Applications/FuckTheRain/FuckTheRain.lua +++ b/Applications/Fuck the Rain.app/Main.lua @@ -1,25 +1,26 @@ -local MineOSInterface = require("MineOSInterface") + +local system = require("System") local GUI = require("GUI") -local event = require("event") -local component = require("component") -local computer = require("computer") +local event = require("Event") +local text = require("Text") --------------------------------------------------------------------------------------------------------- local world if component.isAvailable("debug") then - world = component.debug.getWorld() + world = component.get("debug").getWorld() else GUI.alert("This program requires debug card to run") return end -local container = MineOSInterface.addBackgroundContainer(MineOSInterface.application, "Fuck The Rain") +local workspace = system.getWorkspace() +local container = GUI.addBackgroundContainer(workspace, true, true, "Fuck The Rain") -local lines = string.wrap("This script works as background daemon and checks rain condition in specified interval", 36) +local lines = text.wrap("This script works as background daemon and checks rain condition in specified interval", 36) container.layout:addChild(GUI.textBox(1, 1, 36, #lines, nil, 0xA5A5A5, lines, 1, 0, 0)) -local daemonSwitch = container.layout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x696969, "Daemon enabled:", _G.fuckTheRainTimerID and true or false)).switch +local daemonSwitch = container.layout:addChild(GUI.switchAndLabel(1, 1, 36, 6, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x696969, "Daemon enabled:", _G.fuckTheRainTimerID and true or false)).switch local signalSwitch = container.layout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x696969, "Sound signal:", _G.fuckTheRainSignal)).switch local intervalSlider = container.layout:addChild(GUI.slider(1, 1, 36, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x696969, 1, 10, 2, false, "Interval: ", " s")) @@ -30,8 +31,8 @@ container.layout:addChild(GUI.button(1, 1, 36, 3, 0x444444, 0xFFFFFF, 0x2D2D2D, _G.fuckTheRainSignal = signalSwitch.state and true or nil if daemonSwitch.state then - if not _G.fuckTheRainTimerID then - _G.fuckTheRainTimerID = event.timer(intervalSlider.value, function() + if not _G.fuckTheRainHandler then + _G.fuckTheRainHandler = event.addHandler(function() if world.isRaining() or world.isThundering() then world.setThundering(false) world.setRaining(false) @@ -40,17 +41,17 @@ container.layout:addChild(GUI.button(1, 1, 36, 3, 0x444444, 0xFFFFFF, 0x2D2D2D, computer.beep(1500) end end - end, math.huge) + end, intervalSlider.value) end else - if _G.fuckTheRainTimerID then - event.cancel(_G.fuckTheRainTimerID) - _G.fuckTheRainTimerID = nil + if _G.fuckTheRainHandler then + event.removeHandler(_G.fuckTheRainHandler) + _G.fuckTheRainHandler = nil end end container:remove() - MineOSInterface.application:draw() + workspace:draw() end diff --git a/Applications/GeoScan2/Earth.pic b/Applications/GeoScan2/Earth.pic deleted file mode 100644 index 6d024358be05d0e18b2d34aac4fdb087954929d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1448 zcmZ8hyKZDf5S>$fZ@+H$ea<`DaK_QDCC^xR94tskL8k4b`irl=)YB;c^yHaap(h>^#X5~10){>G zJW7bla&7v9;b=UWicROVQyFA3%>>h&!*O_y;z=6*(~AGI-y?KHvwT|oA(sNGdGePp z$e_SDqMbc)nM8Dd{P+WUc1M6N!(;`*pNk+@P{&Xhp8&91PHxS~&jr%7Lhe1G>2u^- z6h;ms1Bby329vosObUz^&=~FEhztgY=9v(0oFUIAcKiImSkY<2jTJhDWd%Us1ccAh+rTRog&WwL{~HAp>oa zzWK6RYvIrYG(i-J8t^GZ5!XfY6Enk#tROi&m z3syv#ICmFvODe36fci!os@HcE)W6fSS@9j=kp$h6$9O9nVyf4K3$N6c=G6hG;+`Cs zj0G_UbGjK>Of+*d$T8BGT*?Kt$`!B6eJtrhzs?p!iti~mpE&swI%$7?il6@l9|`E@ za?GgIGiGG!F@Av^X0toWcIbY%$NgZ#`#2+P|NHb-_+xU2s8d~r8MzOGNy)7Z0~Q>- zMHy+W-%;5ek|9&6J;IXJkvo_*QNz9L6D?0Xn}=-6#Xa)%?k!2#eID(JEU6gN*JyTe zMi1p3Zg?^u@s76cgPhVLu#`}ql2d{D4Yk?}a%~UvZ&WS*CQ@<3t7=O&?UJ%^#a-91 z0t^@m3%{cl>=1*1Zcf1IDb>3*`$hp9h*d24F?Ejagna4`gwVT$u$MggTTVsFaebvy zj;l2-sx#vFt$xXPt0agChZT||OI9|KZ64)@0{cK7cEjJVQ?YmQY|tg=hGH94yE zTlj_S$u z+;gxcYK4Br&z(zuptFt*(PVYo^{wg;r;#RXdTTC{3bdyLRUxKeiti{?OfeS?x=uaM f|H>tYX3ryCR!~|-4_q`X+aLQXo5@gP>@fZp!+?6y diff --git a/Applications/GeoScan2/Icon.pic b/Applications/GeoScan2/Icon.pic deleted file mode 100644 index 30130057bb9de462a86d79ae57c054403c4f0fc7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 206 zcmW-bu?@m76hwXZ?QaWth?Ic=LP0ttBr3Kr1xrvcLxzAs!W>xyE@^!Ko$lZF?R>p> ziTnjyrWXQ70POYTWqNKL!2=?4had)_I7$fA!fv!`fD9$B@P(mNyKtQs!Hf9tx)}RoynQNr>2QROXG$^()e|A^!mxfD!Hh diff --git a/Applications/GeoScan2/Main.lua b/Applications/GeoScan2/Main.lua deleted file mode 100755 index fecb9be1..00000000 --- a/Applications/GeoScan2/Main.lua +++ /dev/null @@ -1,217 +0,0 @@ - -local component = require("component") -local color = require("color") -local image = require("image") -local buffer = require("doubleBuffering") -local GUI = require("GUI") -local MineOSCore = require("MineOSCore") - --------------------------------------------------------------------------------------------------------------------- - -if not component.isAvailable("geolyzer") then - GUI.alert("This program requires a geolyzer to work!"); return -end - -if not component.isAvailable("hologram") then - GUI.alert("This program requires a hologram projector to work!"); return -end - -component.gpu.setResolution(component.gpu.maxResolution()) -buffer.flush() -local bufferWidth, bufferHeight = buffer.getResolution() - -local resourcesDirectory = MineOSCore.getCurrentScriptDirectory() -local earthImage = image.load(resourcesDirectory .. "Earth.pic") - -local onScreenDataXOffset, onScreenDataYOffset = math.floor(bufferWidth / 2), bufferHeight -local onProjectorDataYOffset = 0 -local scanResult = {horizontalRange = 0, verticalRange = 0} -local application = GUI.application() - --------------------------------------------------------------------------------------------------------------------- - -local function getOpenGLValidColorChannels(cykaColor) - local r, g, b = color.integerToRGB(cykaColor) - return r / 255, g / 255, b / 255 -end - -local function createCube(x, y, z, cykaColor, isVisThrObj) - local cube = component.glasses.addCube3D() - cube.set3DPos(x, y, z) - cube.setVisibleThroughObjects(isVisThrObj) - cube.setColor(getOpenGLValidColorChannels(cykaColor)) - cube.setAlpha(0.23) - return cube -end - -local function glassesCreateCube(x, y, z, cykaColor, text) - local cube = createCube(x, y, z, cykaColor, true) - cube.setVisibleThroughObjects(true) - - local floatingText = component.glasses.addFloatingText() - floatingText.set3DPos(x + 0.5, y + 0.5, z + 0.5) - floatingText.setColor(1, 1, 1) - floatingText.setAlpha(0.6) - floatingText.setText(text) - floatingText.setScale(0.015) -end - -local function createDick(x, y, z, chance, isVisThrObj) - if component.isAvailable("glasses") and math.random(1, 100) <= chance then - createCube(x, y, z, 0xFFFFFF, isVisThrObj) - createCube(x + 1, y, z, 0xFFFFFF, isVisThrObj) - createCube(x + 2, y, z, 0xFFFFFF, isVisThrObj) - createCube(x + 1, y + 1, z, 0xFFFFFF, isVisThrObj) - createCube(x + 1, y + 2, z, 0xFFFFFF, isVisThrObj) - createCube(x + 1, y + 3, z, 0xFFFFFF, isVisThrObj) - createCube(x + 1, y + 5, z, 0xFF8888, isVisThrObj) - end -end - -local function progressReport(value, text) - local width = 40 - local x, y = math.floor(bufferWidth / 2 - width / 2), math.floor(bufferHeight / 2) - GUI.progressBar(x, y, width, 0x00B6FF, 0xFFFFFF, 0xEEEEEE, value, true, true, text, "%"):draw() - buffer.drawChanges() -end - -local function updateData(onScreen, onProjector, onGlasses) - local glassesAvailable = component.isAvailable("glasses") - - if onScreen then buffer.clear(0xEEEEEE) end - if onProjector then component.hologram.clear() end - if onGlasses and glassesAvailable then component.glasses.removeAll() end - - local min, max = tonumber(application.minimumHardnessTextBox.text), tonumber(application.maximumHardnessTextBox.text) - if min and max then - local horizontalRange, verticalRange = math.floor(application.horizontalScanRangeSlider.value), math.floor(application.verticalScanRangeSlider.value) - - for x = -horizontalRange, horizontalRange do - for z = -horizontalRange, horizontalRange do - for y = 32 - verticalRange, 32 + verticalRange do - if scanResult[x] and scanResult[x][z] and scanResult[x][z][y] and scanResult[x][z][y] >= min and scanResult[x][z][y] <= max then - if onScreen then - buffer.semiPixelSet(onScreenDataXOffset + x, onScreenDataYOffset + 32 - y, 0x454545) - end - if onProjector and application.projectorUpdateSwitch.state then - component.hologram.set(horizontalRange + x, math.floor(application.projectorYOffsetSlider.value) + y - 32, horizontalRange + z, 1) - end - if onGlasses and application.glassesUpdateSwitch.state and glassesAvailable then - glassesCreateCube(x, y - 32, z, application.glassesOreColorButton.colors.default.background, "Hardness: " .. string.format("%.2f", scanResult[x][z][y])) - os.sleep(0) - end - end - end - end - end - end -end - -local oldDraw = application.draw -application.draw = function() - updateData(true, false, false) - oldDraw(application) -end - -local panelWidth = 30 -local panelX = bufferWidth - panelWidth + 1 -local buttonX, objectY = panelX + 2, 2 -local buttonWidth = panelWidth - 4 -application:addChild(GUI.panel(panelX, 1, panelWidth, bufferHeight, 0x444444)) - -application.planetImage = application:addChild(GUI.image(buttonX, objectY, earthImage)) -objectY = objectY + application.planetImage.image[2] + 1 - -application:addChild(GUI.label(buttonX, objectY, buttonWidth, 1, 0xFFFFFF, "GeoScan v2.0")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) -objectY = objectY + 2 - -application.horizontalScanRangeSlider = application:addChild(GUI.slider(buttonX, objectY, buttonWidth, 0xFFDB80, 0x000000, 0xFFDB40, 0xBBBBBB, 4, 24, 16, false, "Horizontal scan range: ")) -application.horizontalScanRangeSlider.roundValues = true -objectY = objectY + 3 -application.verticalScanRangeSlider = application:addChild(GUI.slider(buttonX, objectY, buttonWidth, 0xFFDB80, 0x000000, 0xFFDB40, 0xBBBBBB, 4, 32, 16, false, "Vertical show range: ")) -application.verticalScanRangeSlider.roundValues = true -objectY = objectY + 4 - -application:addChild(GUI.label(buttonX, objectY, buttonWidth, 1, 0xFFFFFF, "Rendering properties")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) -objectY = objectY + 2 - -application.minimumHardnessTextBox = application:addChild(GUI.input(buttonX, objectY, 12, 3, 0x262626, 0xBBBBBB, 0xBBBBBB, 0x262626, 0xFFFFFF, tostring(2.7), nil, true)) -application.maximumHardnessTextBox = application:addChild(GUI.input(buttonX + 14, objectY, 12, 3, 0x262626, 0xBBBBBB, 0xBBBBBB, 0x262626, 0xFFFFFF, tostring(10), nil, true)) -objectY = objectY + 3 -application:addChild(GUI.label(buttonX, objectY, buttonWidth, 1, 0xBBBBBB, "Hardness min Hardness max")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) -objectY = objectY + 2 - - -application.projectorScaleSlider = application:addChild(GUI.slider(buttonX, objectY, buttonWidth, 0xFFDB80, 0x000000, 0xFFDB40, 0xBBBBBB, 0.33, 3, component.hologram.getScale(), false, "Projection scale: ")) -application.projectorScaleSlider.onValueChanged = function() - component.hologram.setScale(application.projectorScaleSlider.value) -end -objectY = objectY + 3 -application.projectorYOffsetSlider = application:addChild(GUI.slider(buttonX, objectY, buttonWidth, 0xFFDB80, 0x000000, 0xFFDB40, 0xBBBBBB, 0, 64, 4, false, "Projection Y offset: ")) -application.projectorYOffsetSlider.roundValues = true -objectY = objectY + 3 - -local function setButtonColorFromPalette(button) - local selectedColor = GUI.palette(math.floor(application.width / 2 - 35), math.floor(application.height / 2 - 12), button.colors.default.background):show() - if selectedColor then button.colors.default.background = selectedColor end - application:draw() -end - -local function updateProjectorColors() - component.hologram.setPaletteColor(1, application.color1Button.colors.default.background) -end - -local color1, color2, color3 = component.hologram.getPaletteColor(1), component.hologram.getPaletteColor(2), component.hologram.getPaletteColor(3) -application.color1Button = application:addChild(GUI.button(buttonX, objectY, buttonWidth, 1, color1, 0xBBBBBB, 0xEEEEEE, 0x262626, "Projector color")); objectY = objectY + 1 -application.color1Button.onTouch = function() - setButtonColorFromPalette(application.color1Button) - updateProjectorColors() -end -application.glassesOreColorButton = application:addChild(GUI.button(buttonX, objectY, buttonWidth, 1, 0x0044FF, 0xBBBBBB, 0xEEEEEE, 0x262626, "Glasses ore color")) -application.glassesOreColorButton.onTouch = function() - setButtonColorFromPalette(application.glassesOreColorButton) -end -objectY = objectY + 2 - -application:addChild(GUI.label(buttonX, objectY, buttonWidth, 1, 0xBBBBBB, "Projector update:")) -application.projectorUpdateSwitch = application:addChild(GUI.switch(bufferWidth - 8, objectY, 7, 0xFFDB40, 0xAAAAAA, 0xFFFFFF, true)) -objectY = objectY + 2 -application:addChild(GUI.label(buttonX, objectY, buttonWidth, 1, 0xBBBBBB, "Glasses update:")) -application.glassesUpdateSwitch = application:addChild(GUI.switch(bufferWidth - 8, objectY, 7, 0xFFDB40, 0xAAAAAA, 0xFFFFFF, true)) -objectY = objectY + 2 - -application:addChild(GUI.button(bufferWidth, 1, 1, 1, nil, 0xEEEEEE, nil, 0xFF2222, "X")).onTouch = function() - application:stop() - createDick(math.random(-48, 48), math.random(1, 32), math.random(-48, 48), 100, true) -end - -application:addChild(GUI.button(panelX, bufferHeight - 5, panelWidth, 3, 0x353535, 0xEEEEEE, 0xAAAAAA, 0x262626, "Update")).onTouch = function() - updateData(false, true, true) -end -application.scanButton = application:addChild(GUI.button(panelX, bufferHeight - 2, panelWidth, 3, 0x262626, 0xEEEEEE, 0xAAAAAA, 0x262626, "Scan")) -application.scanButton.onTouch = function() - scanResult = {} - local horizontalRange, verticalRange = math.floor(application.horizontalScanRangeSlider.value), math.floor(application.verticalScanRangeSlider.value) - local total, current = (horizontalRange * 2 + 1) ^ 2, 0 - - buffer.clear(0x0, 0.48) - for x = -horizontalRange, horizontalRange do - scanResult[x] = {} - for z = -horizontalRange, horizontalRange do - scanResult[x][z] = component.geolyzer.scan(x, z) - current = current + 1 - progressReport(math.ceil(current / total * 100), "Scan progress: ") - buffer.drawChanges() - end - end - - application:draw() - updateData(false, true, true) -end - --------------------------------------------------------------------------------------------------------------------- - -buffer.clear(0x0) -application:draw() -application:start() - diff --git a/Applications/GitHub/Icon.pic b/Applications/GitHub/Icon.pic deleted file mode 100644 index 6bd5d5acff46362104e7197227d9d35f4119811e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 191 zcmXwx!41MN3`O(5oirzAU>77LPFy<<2L|W{q>aQ19FSlP?w$lUO%*wMdV2QtbUCN2 z2;pG$gAqRvwE5l;7;L_E6swz6BwI?iv}F&H)y*or{jPyfMPrioD>YhuwMF{nGBs4d a%A=vI_T7#!p+%4$< diff --git a/Applications/GitHub/Main.lua b/Applications/GitHub/Main.lua deleted file mode 100644 index bc6f0951..00000000 --- a/Applications/GitHub/Main.lua +++ /dev/null @@ -1,440 +0,0 @@ - -local computer = require("computer") -local component = require("component") -local GUI = require("GUI") -local buffer = require("doubleBuffering") -local fs = require("filesystem") -local web = require("web") -local json = require("json") -local color = require("color") -local image = require("image") -local base64 = require("base64") -local unicode = require("unicode") -local MineOSInterface = require("MineOSInterface") -local MineOSPaths = require("MineOSPaths") - --------------------------------------------------------------------------------- - -local user -local configPath = MineOSPaths.applicationData .. "/GitHub/Config.cfg" -local config = { - avatarColors = { - [11760002] = 0x3C3C3C, - } -} - -local function saveConfig() - table.toFile(configPath, config) -end - -if fs.exists(configPath) then - config = table.fromFile(configPath) -end - -local addUserShit - --------------------------------------------------------------------------------- - -local mainContainer, window = MineOSInterface.addWindow(GUI.tabbedWindow(1, 1, 108, 33)) - -local titlePanel = window:addChild(GUI.panel(1, 1, window.width, 3, 0x2D2D2D)) -window.tabBar:moveToFront() -window.actionButtons:moveToFront() - -window.backgroundPanel.colors.background = 0xF0F0F0 - -window.tabBar:addItem("Repositories") -window.tabBar:addItem("Gists") -window.tabBar:addItem("Followers") -window.tabBar:addItem("Following") - -local searchInput = window:addChild(GUI.input(9, 2, 14, 1, 0x3C3C3C, 0xB4B4B4, 0x696969, 0x3C3C3C, 0xE1E1E1, "", "Search…")) - -local progressIndicator = window:addChild(GUI.progressIndicator(1, 1, 0x1E1E1E, 0x99FF80, 0x00B640)) - -local userContainer = window:addChild(GUI.container(3, 5, 20, 1)) -local contentContainer = window:addChild(GUI.container(userContainer.localX + userContainer.width + 2, 4, 1, 1)) - -local function request(api) - local url = "https://api.github.com/" .. api - -- GUI.alert(url) - - local data = "" - local success, reason = web.rawRequest( - url, - nil, - { - ["User-Agent"]="Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:42.0) Gecko/20100101 Firefox/42.0", - ["Authorization"]="Basic " .. config.authorization - }, - function(chunk) - data = data .. chunk - - mainContainer:drawOnScreen() - progressIndicator:roll() - end, - math.huge - ) - - if success then - return json:decode(data) - else - return false, "API request failed: " .. tostring(reason) - end -end - -local function avatarDraw(self) - local textColor = 0xFFFFFF - self.color - buffer.drawRectangle(self.x, self.y, self.width, self.height, self.color, textColor, " ") - buffer.drawText(math.floor(self.x + self.width / 2 - unicode.len(self.text) / 2), math.floor(self.y + self.height / 2), textColor, self.text) -end - -local function newAvatar(x, y, width, height, id, name) - local self = GUI.object(x, y, width, height) - - local shortcut = "" - for part in name:gmatch("[^%s]+") do - shortcut = shortcut .. unicode.upper(unicode.sub(part, 1, 1)) - end - - self.text = #shortcut > 0 and shortcut or unicode.upper(unicode.sub(name, 1, 1)) - self.draw = avatarDraw - self.color = config.avatarColors[id] - if not self.color then - config.avatarColors[id] = color.HSBToInteger(math.random(0, 360), math.random(100) / 100, 1) - saveConfig() - end - - return self -end - -local function repositoryGUI(repositoryName, branches) - progressIndicator.active = true - - local reason - if not branches then - branches, reason = request("repos/" .. user.login .. "/" .. repositoryName .. "/branches") - if not branches then - GUI.alert(reason) - - progressIndicator.active = false - mainContainer:drawOnScreen() - return - end - end - - contentContainer:removeChildren() - - local path = "" - - local branchesComboBox = contentContainer:addChild(GUI.comboBox(1, 2, 14, 1, 0xE1E1E1, 0x2D2D2D, 0xC3C3C3, 0x787878)) - - local pathLayout = contentContainer:addChild(GUI.layout(branchesComboBox.localX + branchesComboBox.width + 2, 2, contentContainer.width, 10, 1, 1)) - pathLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP) - pathLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL) - pathLayout:setSpacing(1, 1, 0) - - for i = 1, #branches do - branchesComboBox:addItem(branches[i].name) - end - - local previewContainer = contentContainer:addChild(GUI.container(1, 4, contentContainer.width - 2, contentContainer.height - 3)) - - local fillList, fillPath, fillCode - - fillList = function() - progressIndicator.active = true - - local result, reason = request("repos/" .. user.login .. "/" .. repositoryName .. "/contents/" .. path .. "?ref=" .. branchesComboBox:getItem(branchesComboBox.selectedItem).text) - if result then - if result.type == "file" then - local lines = {} - for line in base64.decode(result.content:gsub("\r\n", "\n")):gmatch("[^\n]+") do - line = line:gsub("\t", " ") - table.insert(lines, line) - end - - local codeView = previewContainer:addChild(GUI.codeView(1, 1, previewContainer.width, previewContainer.height, 1, 1, 1, {}, {}, GUI.LUA_SYNTAX_PATTERNS, GUI.LUA_SYNTAX_COLOR_SCHEME, fs.extension(result.name) == ".lua", lines)) - - else - -- Sort files alphabetically - local files = {} - local i = 1 - while i <= #result do - if result[i].type ~= "dir" then - table.insert(files, result[i]) - table.remove(result, i) - else - i = i + 1 - end - end - - table.sort(files, function(a, b) return unicode.lower(a.name) < unicode.lower(b.name) end) - - i = 1 - while i <= #files do - table.insert(result, files[i]) - table.remove(files, i) - end - - -- Fill files container - local function hyperlinkDraw(self) - buffer.drawRectangle(self.x, self.y, self.width, self.height, self.pressed and self.pressedColor or self.backgroundColor, self.textColor, " ") - buffer.drawText(self.x + 2, math.floor(self.y + self.height / 2), self.textColor, (self.node.type == "dir" and "■ " or "□ ") .. self.node.name) - end - - local function hyperlinkEventHandler(mainContainer, self, e1) - if e1 == "touch" then - self.pressed = true - mainContainer:drawOnScreen() - - -- if self.node.type == "dir" then - path = path .. web.encode(self.node.name) .. "/" - fillPath() - fillList() - -- else - - -- end - end - end - - local function newHyperlink(x, y, width, height, backgroundColor, pressedColor, textColor, node) - local object = GUI.object(x, y, width, height) - - object.pressed = false - object.backgroundColor = backgroundColor - object.pressedColor = pressedColor - object.textColor = textColor - object.draw = hyperlinkDraw - object.eventHandler = hyperlinkEventHandler - object.node = node - - return object - end - - previewContainer:removeChildren() - - local y, step = 1, false - for i = 1, #result do - local hyperlink = previewContainer:addChild(newHyperlink(1, y, previewContainer.width, 3, step and 0xD2D2D2 or 0xE1E1E1, 0xC3C3C3, result[i].type == "dir" and 0x3C3C3C or 0x787878, result[i])) - - y, step = y + hyperlink.height, not step - end - end - else - GUI.alert(reason) - end - - progressIndicator.active = false - mainContainer:drawOnScreen() - end - - fillPath = function() - pathLayout:removeChildren() - - pathLayout:addChild(GUI.button(1, 1, unicode.len(repositoryName), 1, nil, 0x9949BF, nil, 0x332480, repositoryName)).onTouch = function() - path = "" - fillPath() - fillList() - end - - pathLayout:addChild(GUI.text(1, 1, 0xC3C3C3, "/")) - - local segments = fs.segments(path) - for i = 1, #segments - 1 do - pathLayout:addChild(GUI.button(1, 1, unicode.len(segments[i]), 1, nil, 0x9949BF, nil, 0x332480, segments[i])).onTouch = function() - path = table.concat(segments, "/", 1, i) .. "/" - fillPath() - fillList() - end - pathLayout:addChild(GUI.text(1, 1, 0xC3C3C3, "/")) - end - - if #segments > 0 then - pathLayout:addChild(GUI.text(1, 1, 0x787878, segments[#segments])) - end - end - - branchesComboBox.onItemSelected = function() - - end - - fillPath() - fillList() -end - -local function repositoryDraw(self) - buffer.drawRectangle(self.x, self.y, self.width, self.height, self.pressed and 0xE1E1E1 or 0xFFFFFF, 0x969696, " ") - buffer.drawText(self.x + 2, self.y + 1, 0x004980, self.name) - buffer.drawText(self.x + 2, self.y + 3, 0x969696, self.lines[1]) - buffer.drawText(self.x + 2, self.y + 4, 0x969696, self.lines[2]) - buffer.drawText(self.x + 2, self.y + 6, 0x3C3C3C, self.stats) -end - -local function repositoryEventHandler(mainContainer, self, e1) - if e1 == "touch" then - self.pressed = true - mainContainer:drawOnScreen() - - repositoryGUI(self.name) - end -end - -local function newRepository(x, y, width, repository) - local self = GUI.object(x, y, width, 8) - - self.pressed = false - self.name = repository.name - - self.lines = string.wrap(repository.description or "No description provided", width - 4) - if self.lines[2] then - self.lines[2] = string.limit(table.concat(self.lines, " ", 2), width - 4) - end - - self.stats = (repository.language and "• " .. repository.language .. " " or "") .. "* " .. repository.stargazers_count .. " # " .. repository.forks_count - - self.draw = repositoryDraw - self.eventHandler = repositoryEventHandler - - return self -end - -local function repositoriesGUI(forks, page) - local startX, startY = 1, 2 - local width = 40 - local perHeight = math.floor((contentContainer.height - 3) / 9) - local perWidth = math.floor((contentContainer.width - startX) / width) - local perPage = perWidth * perHeight - - progressIndicator.active = true - - local result, reason = request("users/" .. user.login .. "/repos?page=" .. page .. "&per_page=" .. perPage .. "&sort=updated") - if result then - contentContainer:removeChildren() - - local x, y, maxHeightPerLine = startX, startY, 0 - for i = 1, math.min(#result, perPage) do - -- if forks and result[i].fork or not forks and not result[i].fork then - local repository = contentContainer:addChild(newRepository(x, y, width, result[i])) - maxHeightPerLine = math.max(maxHeightPerLine, repository.height) - - x = x + repository.width + 2 - if x > contentContainer.width - 2 then - x, y = startX, y + maxHeightPerLine + 1 - maxHeightPerLine = 0 - end - -- end - end - else - GUI.alert(reason) - end - - progressIndicator.active = false - mainContainer:drawOnScreen() -end - -local function loginGUI() - local loginContainer = window:addChild(GUI.container(1, 1, window.width, window.height)) - window.actionButtons:moveToFront() - - loginContainer:addChild(GUI.panel(1, 1, loginContainer.width, loginContainer.height, 0xF0F0F0)) - - local layout = loginContainer:addChild(GUI.layout(1, 1, loginContainer.width, loginContainer.height, 1, 1)) - - local try, again - - try = function() - progressIndicator.active = true - layout:removeChildren() - - layout:addChild(GUI.text(1, 1, 0x969696, "Logging in...")) - mainContainer:drawOnScreen() - - local result, reason = request("users/" .. config.user) - if result and result.id then - loginContainer:remove() - - user = result - addUserShit() - repositoriesGUI(false, 1) - else - GUI.alert("Incorrect login or password") - user = nil - config.authorization = nil - saveConfig() - - again() - end - - progressIndicator.active = false - mainContainer:drawOnScreen() - end - - again = function() - layout:removeChildren() - - local userInput = layout:addChild(GUI.input(1, 1, 26, 3, 0xE1E1E1, 0x787878, 0xB4B4B4, 0xE1E1E1, 0x2D2D2D, config.user or "", "Username")) - local passwordInput = layout:addChild(GUI.input(1, 1, 26, 3, 0xE1E1E1, 0x787878, 0xB4B4B4, 0xE1E1E1, 0x2D2D2D, "", "Password", false, "*")) - local submitButton = layout:addChild(GUI.button(1, 1, 26, 3, 0xC3C3C3, 0xFFFFFF, 0x969696, 0xFFFFFF, "Login")) - - submitButton.onTouch = function() - config.user = userInput.text - config.authorization = base64.encode(userInput.text .. ":" .. passwordInput.text) - saveConfig() - - try() - end - - mainContainer:drawOnScreen() - end - - if config.authorization then - try() - else - again() - end -end - -addUserShit = function() - userContainer:removeChildren() - - local y = 1 - - local avatar = userContainer:addChild(newAvatar(1, y, userContainer.width, userContainer.width / 2 - 1, user.id, user.name or user.login)) - y = y + avatar.height + 1 - - userContainer:addChild(GUI.text(1, y, 0x2D2D2D, user.login)) - y = y + 2 - - if user.name then - userContainer:addChild(GUI.text(1, y - 1, 0x969696, user.name)) - y = y + 1 - end - - if user.bio then - local lines = string.wrap(user.bio, userContainer.width) - local textBox = userContainer:addChild(GUI.textBox(1, y, userContainer.width, #lines, nil, 0x969696, lines, 1)) - textBox.eventHandler = nil - y = y + #lines + 1 - end - - userContainer:addChild(GUI.roundedButton(2, y, userContainer.width - 2, 1, 0xB4B4B4, 0xFFFFFF, 0x969696, 0xFFFFFF, "Logout")).onTouch = function() - config.authorization = nil - saveConfig() - - loginGUI() - end -end - -local function calculateSizes() - userContainer.height = window.height - 4 - contentContainer.width, contentContainer.height = window.width - userContainer.width - 4, window.height - 3 - progressIndicator.localX = window.width - progressIndicator.width - - titlePanel.width = window.width - window.tabBar.width = window.width - progressIndicator.width - window.actionButtons.width - searchInput.width - 8 - window.tabBar.localX = searchInput.localX + searchInput.width + 2 -end - -calculateSizes() -loginGUI() diff --git a/Applications/Graph2/Icon.pic b/Applications/Graph.app/Icon.pic similarity index 100% rename from Applications/Graph2/Icon.pic rename to Applications/Graph.app/Icon.pic diff --git a/Applications/Graph2/Main.lua b/Applications/Graph.app/Main.lua similarity index 82% rename from Applications/Graph2/Main.lua rename to Applications/Graph.app/Main.lua index b0dc4b8c..112ee521 100644 --- a/Applications/Graph2/Main.lua +++ b/Applications/Graph.app/Main.lua @@ -1,14 +1,13 @@ -require("advancedLua") -local fs = require("filesystem") -local buffer = require("doubleBuffering") +local text = require("Text") +local number = require("Number") +local screen = require("Screen") local GUI = require("GUI") -local unicode = require("unicode") -local MineOSInterface = require("MineOSInterface") +local system = require("System") --------------------------------------------------------------------------------------------------------- -local application, window = MineOSInterface.addWindow(GUI.filledWindow(1, 1, 110, 25, 0xF0F0F0)) +local workspace, window = system.addWindow(GUI.filledWindow(1, 1, 110, 25, 0xF0F0F0)) local yDependencyString = "math.sin(x)" local xOffset, yOffset, xDrag, yDrag, points = 0, 0, 1, 1 @@ -30,23 +29,23 @@ window.actionButtons:moveToFront() local graph = window:addChild(GUI.object(1, 4, window.width, window.height - 3)) graph.draw = function(graph) - local x1, x2, y1, y2 = buffer.getDrawLimit() - buffer.setDrawLimit(graph.x, graph.y, graph.x + graph.width - 1, graph.y + graph.height - 1) + local x1, x2, y1, y2 = screen.getDrawLimit() + screen.setDrawLimit(graph.x, graph.y, graph.x + graph.width - 1, graph.y + graph.height - 1) local xCenter, yCenter = graph.x + xOffset + graph.width / 2 - 1, graph.y + yOffset + graph.height / 2 - 1 - buffer.drawSemiPixelLine(math.floor(graph.x), math.floor(yCenter * 2), math.floor(graph.x + graph.width - 1), math.floor(yCenter * 2), 0xD2D2D2) - buffer.drawSemiPixelLine(math.floor(xCenter), math.floor(graph.y * 2 - 1), math.floor(xCenter), math.floor(graph.y + graph.height - 1) * 2, 0xD2D2D2) + screen.drawSemiPixelLine(math.floor(graph.x), math.floor(yCenter * 2), math.floor(graph.x + graph.width - 1), math.floor(yCenter * 2), 0xD2D2D2) + screen.drawSemiPixelLine(math.floor(xCenter), math.floor(graph.y * 2 - 1), math.floor(xCenter), math.floor(graph.y + graph.height - 1) * 2, 0xD2D2D2) for i = 1, #points - 1 do local x1, x2, y1, y2 = math.floor(xCenter + points[i].x), math.floor(yCenter - points[i].y + 1) * 2, math.floor(xCenter + points[i + 1].x), math.floor(yCenter - points[i + 1].y + 1) * 2 - buffer.drawSemiPixelLine(x1, x2, y1, y2, 0x0) + screen.drawSemiPixelLine(x1, x2, y1, y2, 0x0) if switchAndLabel.switch.state then - buffer.semiPixelSet(x1, x2, 0x66DB80) + screen.semiPixelSet(x1, x2, 0x66DB80) end end - buffer.setDrawLimit(x1, x2, y1, y2) + screen.setDrawLimit(x1, x2, y1, y2) end local function update() @@ -78,7 +77,7 @@ local function update() end functionButton.onTouch = function() - local container = MineOSInterface.addBackgroundContainer(window, "Set function f(x)") + local container = GUI.addBackgroundContainer(workspace, true, true, "Set function f(x)") local inputField = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xEEEEEE, 0x666666, 0x666666, 0xEEEEEE, 0x262626, yDependencyString, "f(x)", false)) inputField.onInputFinished = function() if inputField.text then @@ -86,16 +85,16 @@ functionButton.onTouch = function() update() container:remove() - application:draw() + workspace:draw() end end - application:draw() + workspace:draw() end scaleSlider.onValueChanged = function() update() - application:draw() + workspace:draw() end rangeSlider.onValueChanged = scaleSlider.onValueChanged precisionSlider.onValueChanged = scaleSlider.onValueChanged @@ -109,12 +108,12 @@ window.onResize = function(width, height) update() end -graph.eventHandler = function(application, graph, e1, e2, e3, e4, e5) +graph.eventHandler = function(workspace, graph, e1, e2, e3, e4, e5) if e1 == "touch" then xDrag, yDrag = e3, e4 elseif e1 == "drag" then xOffset, yOffset = xOffset + (e3 - xDrag), yOffset + (e4 - yDrag) - application:draw() + workspace:draw() xDrag, yDrag = e3, e4 elseif e1 == "scroll" then @@ -126,14 +125,14 @@ graph.eventHandler = function(application, graph, e1, e2, e3, e4, e5) end update() - application:draw() + workspace:draw() end end --------------------------------------------------------------------------------------------------------- update() -application:draw() +workspace:draw() diff --git a/Applications/GuessWord/About/English.txt b/Applications/GuessWord/About/English.txt deleted file mode 100644 index c88e06bd..00000000 --- a/Applications/GuessWord/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Мини-игра "Угадай Слова" от автора Newbie с форума ComputerCraft.ru. Игра на данный момент имеет базу из 300 вопросов, в процессе вам случайным образом подбирается слово, появляется экран, где есть ячейки, за которыми спрятаны буквы. Над ними находится вопрос-подсказка. Вы угадываете буквы путем нажатия на экранной клавиатуре на букву - если буква угадана, то кнопка примет зеленый цвет, если нет - красный. Также угаданная буква помещается сразу в свою ячейку. Если одинаковых букв в слове больше одной, то они также откроются в своих ячейках. Удачного мозголомства! \ No newline at end of file diff --git a/Applications/GuessWord/About/Russian.txt b/Applications/GuessWord/About/Russian.txt deleted file mode 100644 index c88e06bd..00000000 --- a/Applications/GuessWord/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Мини-игра "Угадай Слова" от автора Newbie с форума ComputerCraft.ru. Игра на данный момент имеет базу из 300 вопросов, в процессе вам случайным образом подбирается слово, появляется экран, где есть ячейки, за которыми спрятаны буквы. Над ними находится вопрос-подсказка. Вы угадываете буквы путем нажатия на экранной клавиатуре на букву - если буква угадана, то кнопка примет зеленый цвет, если нет - красный. Также угаданная буква помещается сразу в свою ячейку. Если одинаковых букв в слове больше одной, то они также откроются в своих ячейках. Удачного мозголомства! \ No newline at end of file diff --git a/Applications/GuessWord/GuessWord.lua b/Applications/GuessWord/GuessWord.lua deleted file mode 100644 index d2e05c87..00000000 --- a/Applications/GuessWord/GuessWord.lua +++ /dev/null @@ -1,588 +0,0 @@ -local term = require("term") -local event = require("event") -local computer = require("computer") -local component = require("component") -local unicode = require("unicode") -local fs = require("filesystem") -local gpu = component.gpu -local serialization = require("serialization") -local xSize, ySize = gpu.getResolution() -local width = 63 -local height = 25 -local xPosCells -local tempXPosCells -local xPosTitle -local tempXPosTitle -local yPosTitle = 8 -local yPosCells = 10 -local cellsXPos = {} -local title -local words = {} -local word1 = {} -local word2 = {'_','_','_','_','_','_','_','_','_'} -local score = 0 -local point = 0 -local heard = 5 -local sameLetter = false -local noWords = false -local nicknames -local records -local name -local count -local heardPlus -local tempRandom -local record = 0 -local play = true -local colors = { - background = 0x7A8B8B, - button = 0x00688B, - textButton = 0xE0FFFF, - input = 0xBBFFFF, - cell = 0x2F4F4F, - text = 0x000000, - heard = 0xFF0000, - correctLetter = 0x7CFC00, - incorrectLetter = 0xB22222, - defBg = 0x000000, - defText = 0xFFFFFF -} ---Наша клавиатура где (x,y,символ, надпись на клавиатуре) -local keyboard = { - {8, 16,"й"," Й "}, - {12, 16, "ц", " Ц "}, - {16, 16, "у", " У "}, - {20, 16 , "к", " К "}, - {24, 16, "е", " Е "}, - {28, 16, "н", " Н "}, - {32, 16, "г", " Г "}, - {36, 16, "ш", " Ш "}, - {40, 16, "щ", " Щ "}, - {44, 16, "з", " З "}, - {48, 16, "х", " Х "}, - {52, 16, "ъ", " Ъ "}, - {10, 18, "ф", " Ф "}, - {14, 18, "ы", " Ы "}, - {18, 18, "в", " В "}, - {22, 18, "а", " А "}, - {26, 18, "п", " П "}, - {30, 18, "р", " Р "}, - {34, 18, "о", " О "}, - {38, 18, "л", " Л "}, - {42, 18, "д", " Д "}, - {46, 18, "ж", " Ж "}, - {50, 18, "э", " Э "}, - {14, 20, "я", " Я "}, - {18, 20, "ч", " Ч "}, - {22, 20, "с", " С "}, - {26, 20, "м", " М "}, - {30, 20, "и", " И "}, - {34, 20, "т", " Т "}, - {38, 20, "ь", " Ь "}, - {42, 20, "б", " Б "}, - {46, 20, "ю", " Ю "} -} - -local selectKey - -gpu.setResolution(width, height) - -local pathToWords = "words.txt" -local function loadWords() --Загружаем слова - local bool = true - gpu.setBackground(colors.background) - if fs.exists(pathToWords) then - local array = {} - local file = io.open(pathToWords, "r") - local str = file:read("*a") - array = serialization.unserialize(str) - file:close() - words = array - else - if component.isAvailable("internet") then - os.execute("pastebin get rc7qrrHA words.txt") - term.clear() - gpu.set(18, 12, "Загружен файл со словами!") - os.sleep(5) - term.clear() - loadWords() - else - term.clear() - gpu.set(4,12, "Вставьте Интернет карту или скачайте words.txt вручную.") - gpu.set(10,13,"По ссылке http://pastebin.com/rc7qrrHA") - gpu.setBackground(colors.button) - gpu.setForeground(colors.textButton) - gpu.set(4,24,"[<<Назад]") - gpu.setBackground(colors.background) - gpu.setForeground(colors.text) - while bool do - local e = {event.pull("touch")} - if e[4] == 24 then - if e[3]>3 and e[3]<14 then - play = false - noWords = true - bool = false - end - end - end - end - end -end ---Берем рандомное слово -local function getRandomWord() - local randomN = math.modf(math.random(1,#words)) - if tempRandom ~= randomN then --Проверка чтоб небыло 2 подряд - title = words[randomN].title - word1 = words[randomN].word - else - getRandomWord() - end - tempRandom = randomN -end - -local pathToRecords = "recordsGtW.txt" --путь к файлу с рекордами -local function saveRecord() --Сохраняем рекорды - local file = io.open(pathToRecords, "w") - local array = {["nicknames"] = nicknames, ["records"] = records} - file:write(serialization.serialize(array)) - file:close() -end -local function saveScore() --сохраняем наши заработанные очки - for i = 1, #nicknames do - if name == nicknames[i] then - if score >= record then - records[i] = score - end - end - end - saveRecord() -end -local function loadRecord() --Загружаем рекорды - if fs.exists(pathToRecords) then - local array = {} - local file = io.open(pathToRecords, "r") - local str = file:read("*a") - array = serialization.unserialize(str) - file:close() - nicknames = array.nicknames - records = array.records - else --или создаем новые дефолтные пустые таблицы - fs.makeDirectory(fs.path(pathToRecords)) - nicknames = {} - records = {} - saveRecord() - end -end -local function checkName(name) --Проверка на наличие имени в базе - for i =1, #nicknames do - if name == nicknames[i] then - record = records[i] - return false - end - end - return true -end -local function addPlayer() --Создаем учетку пользователю если его нет в базе - if checkName(name) then - table.insert(nicknames, name) - table.insert(records, record) - saveRecord() - end -end -local function getXPosTitle() --Получаем х позицию вопроса - tempXPosTitle = unicode.len(title) - tempXPosTitle = width - tempXPosTitle - xPosTitle = math.modf(tempXPosTitle/2) - tempXPosTitle = xPosTitle -end - -local function getXPosCells() --Получаем х позицию ячеек - tempXPosCells = #word1 - tempXPosCells = tempXPosCells*5 - 1 - tempXPosCells = width - tempXPosCells - xPosCells = tempXPosCells/2 - tempXPosCells = xPosCells -end - -getXPosCells() - -local function paintMenu() --Отрисовываем меню - gpu.setResolution(width, height) - gpu.setBackground(colors.background) - term.clear() - gpu.setForeground(colors.text) - - gpu.set(27, 3, "Угадай-Ка") - gpu.setForeground(colors.textButton) - gpu.setBackground(colors.button) - gpu.set(25, 15, "[Начать игру]") - gpu.set(25, 17, "[Топ Лидеров]") - gpu.set(27, 19,"[Правила]") - gpu.set(28, 21, "[Выход]") - gpu.setForeground(colors.text) -end - -local function paintScene() --Отрисовываем игровой экран - getXPosCells() - getXPosTitle() - gpu.setBackground(colors.background) - term.clear() - gpu.set(xPosTitle, yPosTitle, title) - for i=1, #word1 do - table.insert(cellsXPos, tempXPosCells) - gpu.setBackground(colors.cell) - gpu.setForeground(colors.text) - gpu.set(tempXPosCells, yPosCells, " ") - tempXPosCells = tempXPosCells + 5 - gpu.setBackground(colors.background) - end - - for i=1, #keyboard do - gpu.setBackground(colors.button) - gpu.set(keyboard[i][1], keyboard[i][2], keyboard[i][4]) - gpu.setBackground(colors.background) - end - local tempN = unicode.len(name) - tempN = width - (tempN + 17) - gpu.set(tempN,2,name.." :Текущий игрок") - gpu.set(2,2,"Ваш рекорд: "..record) - gpu.set(49,3, " :Ваши жизни") - gpu.setForeground(colors.heard) - gpu.set(44,3, "❤x"..heard) - gpu.setForeground(colors.text) - gpu.set(2,3,"Текущий счет: "..score) - -end - -local function paintRules() --Отрисовываем правила - local bool = true - gpu.setBackground(colors.background) - term.clear() - gpu.setForeground(colors.text) - gpu.set(25,7,"Правила игры!") - gpu.set(4,11," Доброго времени суток, уважаемый игрок!") - gpu.set(4,12," Правила <<Угадай-Ки>> очень просты, перед вами будет") - gpu.set(4,13,"n-количество ячеек за которыми буквы. Сверху") - gpu.set(4,14,"подсказка. Чтоб выбрать букву нажмите ее на экранной") - gpu.set(4,15,"клавиатуре. Если угадаете она появится в поле и на") - gpu.set(4,16,"ЭК станет зеленной, неугадаете красной. Есть 4 режима.") - gpu.set(4,17,"Если не угадали букву минус жизнь. Каждое угаданное слово") - gpu.set(4,18,"дает свое количество очков в зависимости от режима игры.") - gpu.set(4,19,"Каждая угаданая подряд буква умножает очки на кол-во") - gpu.set(4,20,"угаданых букв подряд. Удачи в игре!!") - gpu.setBackground(colors.button) - gpu.setForeground(colors.textButton) - gpu.set(4,24,"[<<Назад]") - gpu.setBackground(colors.background) - gpu.setForeground(colors.text) - while bool do - local e = {event.pull("touch")} - if e[4] == 24 then - if e[3]>3 and e[3]<14 then - bool = false - guessTW() - end - end - end -end - -local function clearLine(a) --Просто отчиистка линии для сокращения кода - term.setCursor(1,a) - term.clearLine() -end - -local function guessTheWord() --Наш алгоритм для сранения букв и работа с нимм - local goodLetter = false - local haveSpace = false - local key = selectKey - local letter = key[3] - local tempScore - local bool = true - - for i = 1, #word1 do - if word1[i] == letter then - if word1[i] ~= word2[i] then - point = point + 1 - tempScore = point*count - score = score + tempScore - gpu.set(16,3, tostring(score)) - if record>=score then - gpu.set(14,2, tostring(record)) - else - record = score - gpu.set(14,2, tostring(record)) - end - goodLetter = true - gpu.set(25,12,"Верная буква!") - elseif word1[i] == word2[i] then - sameLetter = true - end - word2[i] = letter - gpu.setBackground(colors.cell) - gpu.setForeground(colors.textButton) - gpu.set(cellsXPos[i],10, key[4]) - gpu.setForeground(colors.text) - gpu.setBackground(colors.correctLetter) - gpu.set(key[1],key[2],key[4]) - gpu.setBackground(colors.background) - gpu.setForeground(colors.text) - clearLine(12) - gpu.set(25,12,"Верная буква!") - end - if word2[i] == "_" then - haveSpace = true - end - end - - if goodLetter then - if not haveSpace then - heard = heard + heardPlus - gpu.setForeground(colors.heard) - gpu.set(47,3," ") - gpu.set(47,3, tostring(heard)) - gpu.setForeground(colors.text) - clearLine(12) - if heardPlus == 0 then - gpu.set(18, 12,"Слово отгадано, продолжим?") - elseif heardPlus == 2 then - gpu.set(7, 12,"Слово отгадано, вы получили две жизни, продолжим?") - else - gpu.set(6, 12,"Слово отгадано, вы получили одну жизнь, продолжим?") - end - gpu.setForeground(colors.textButton) - gpu.setBackground(colors.button) - gpu.set(35,14,"[Далее >>]") - gpu.set(18,14,"[Выход]") - gpu.setForeground(colors.text) - while bool do - local e = {event.pull("touch")} - if e[4] == 14 then - if e[3]>17 and e[3]<26 then - play = false - bool = false - heardScore = heard * count * point - score = score + heardScore - saveScore() - score = 0 - guessTW() - - elseif e[3]>34 and e[3]<44 then - bool = false - saveScore() - game() - - end - end - end - end - elseif sameLetter then - clearLine(12) - gpu.set(21,12,"Эта буква уже введена") - sameLetter = false - else - point = 0 - clearLine(12) - gpu.set(24,12,"Неверная буква!") - gpu.setBackground(colors.incorrectLetter) - gpu.set(key[1],key[2],key[4]) - gpu.setBackground(colors.background) - heard = heard - 1 - if heard ~= 0 then - gpu.setForeground(colors.heard) - gpu.set(47,3," ") - gpu.set(47,3, tostring(heard)) - gpu.setForeground(colors.text) - else - term.clear() - gpu.set(15,11,"Игра окончена!!! Ваш счет: "..tostring(score)) - score = 0 - os.sleep(8) - play = false - guessTW() - end - end -end - - -local function sortTop() --Сортируем Топ игроков - for i=1, #records do - for j=1, #records-1 do - if records[j] < records[j+1] then - local r = records[j+1] - local n = nicknames[j+1] - records[j+1] = records[j] - nicknames[j+1] = nicknames[j] - records[j] = r - nicknames[j] = n - end - end - end - saveRecord() -end -function printRecords() --Выводим рекорды на экран - local bool = true - sortTop() - gpu.setBackground(colors.background) - term.clear() - local xPosName = 15 - local xPosRecord = 40 - local yPos = 2 - loadRecord() - gpu.setForeground(colors.text) - gpu.set(25,2,"Toп Лидеров") - gpu.setForeground(colors.textButton) - if #nicknames <= 15 then - for i = 1, #nicknames do - yPos= yPos+1 - gpu.set(xPosName, yPos, nicknames[i] ) - gpu.set(xPosRecord, yPos, tostring(records[i])) - end - else - for i = 1, 15 do - yPos= yPos+1 - gpu.set(xPosName, yPos, nicknames[i] ) - gpu.set(xPosRecord, yPos, tostring(records[i])) - end - end - gpu.setBackground(colors.button) - gpu.set(4,24,"[<<Назад]") - gpu.setBackground(colors.background) - while bool do - local e = {event.pull("touch")} - if e[4] == 24 then - if e[3]>3 and e[3]<14 then - bool = false - guessTW() - end - end - end -end - -function game() --Наша игра - cellsXPos = {} - word2 = {'_','_','_','_','_','_','_','_','_'} - term.clear() - getRandomWord() - paintScene() - while play do - local e = {event.pull("touch")} - for i=1, #keyboard do - if e[4] == keyboard[i][2] then - if e[3] > keyboard[i][1]-1 and e[3] < keyboard[i][1]+3 then - selectKey = keyboard[i] - guessTheWord() - end - end - end - end -end - -local function selectComplexity() --Выбор уровня сложности - local bool = true - gpu.setBackground(colors.background) - term.clear() - gpu.setBackground(colors.button) - gpu.setForeground(colors.textButton) - gpu.set(27,10,"[Хардкор]") - gpu.set(27,13,"[Сложная]") - gpu.set(27,16,"[Средняя]") - gpu.set(28,19,"[Легко]") - gpu.set(4,24,"[<<Назад]") - gpu.setBackground(colors.background) - gpu.setForeground(colors.text) - gpu.set(22,8,"Выберите сложность:") - gpu.set(9,11,"Всего 10 жизней на игру и за букву 100 очков!") - gpu.set(5,14,"2 жизни в начале и за букву 50 очков, за слово жизнь!") - gpu.set(6,17,"5 жизней в начале и за букву 10 очков, за слово жизнь!") - gpu.set(6,20,"10 жизней в начале и за букву 2 очка, за слово 2 жизни!") - - while bool do - local e = {event.pull("touch")} - if e[4] == 10 then - if e[3]>26 and e[3]<36 then - bool = false - heard = 10 - heardPlus = 0 - count = 100 - game() - end - elseif e[4] == 13 then - if e[3]>26 and e[3]<36 then - bool = false - heard = 2 - heardPlus = 1 - count = 50 - game() - end - elseif e[4] == 16 then - if e[3]>26 and e[3]<36 then - bool = false - heard = 5 - heardPlus = 1 - count = 10 - game() - end - elseif e[4] == 19 then - if e[3]>27 and e[3]<35 then - bool = false - heard = 10 - heardPlus = 2 - count = 2 - game() - end - elseif e[4] == 24 then - if e[3]>3 and e[3]<14 then - bool = false - - guessTW() - end - end - end -end - - -function guessTW() -- Запуск нашей игры - record = 0 - loadRecord() - loadWords() - paintMenu() - while true do - local e = {event.pull("touch")} - if e[4] == 15 then - if e[3]>24 and e[3]<33 then - if not noWords then - name = e[6] - addPlayer(name) - for i = 1, #nicknames do - if name == nicknames[i] then - record = records[i] - end - end - play = true - point = 0 - selectComplexity() - end - end - elseif e[4] == 17 then - if e[3]>24 and e[3]<37 then - sortTop() - printRecords() - end - elseif e[4] == 19 then - if e[3]>26 and e[3]<36 then - paintRules() - end - elseif e[4] == 21 then - if e[3]>27 and e[3]<35 then - gpu.setForeground(colors.defText) - gpu.setBackground(colors.defBg) - gpu.setResolution(xSize,ySize) - term.clear() - quit = true - break - end - end - if quit then break end - end -end - -guessTW() \ No newline at end of file diff --git a/Applications/GuessWord/Icon.pic b/Applications/GuessWord/Icon.pic deleted file mode 100644 index b59268a0bb4047920371ca6f6e93f74774e607d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 121 zcmeZw_H<+8U}0on;80+^&d9*X$ic|O%)-jX&T(NDP#~C*iIM%nVknF8(X<984 selection.from and index < selection.to then - buffer.drawRectangle(x - object.offset, y, object.elementWidth, 1, colors.selectionBetween, colors.selectionText, " ") + screen.drawRectangle(x - object.offset, y, object.elementWidth, 1, colors.selectionBetween, colors.selectionText, " ") textColor = colors.selectionBetweenText end - buffer.drawText(x, y, textColor, object.asChar and string.char(bytes[index]) or string.format("%02X", bytes[index])) + screen.drawText(x, y, textColor, object.asChar and string.char(bytes[index]) or string.format("%02X", bytes[index])) else return object end @@ -75,7 +83,7 @@ local function byteFieldDraw(object) local lastLineIndex = index - 1 if lastLineIndex >= selection.from and lastLineIndex < selection.to then - buffer.drawRectangle(object.x - object.offset, y + 1, object.width, 1, colors.selectionBetween, colors.selectionText, " ") + screen.drawRectangle(object.x - object.offset, y + 1, object.width, 1, colors.selectionBetween, colors.selectionText, " ") end x, y = object.x, y + object.elementHeight @@ -84,22 +92,22 @@ local function byteFieldDraw(object) return object end -local function byteFieldEventHandler(application, object, e1, e2, e3, e4, e5) +local function byteFieldEventHandler(workspace, object, e1, e2, e3, e4, e5) if e1 == "touch" or e1 == "drag" then if e5 == 1 then - local menu = GUI.addContextMenu(application, e3, e4) + local menu = GUI.addContextMenu(workspace, e3, e4) menu:addItem("Select all").onTouch = function() selection.from = 1 selection.to = #bytes - application:draw() + workspace:draw() end menu:addSeparator() menu:addItem("Edit").onTouch = function() - local container = MineOSInterface.addBackgroundContainer(application, "Fill byte range [" .. selection.from .. "; " .. selection.to .. "]") + local container = system.addBackgroundContainer(workspace, "Fill byte range [" .. selection.from .. "; " .. selection.to .. "]") local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x666666, 0x666666, 0xE1E1E1, 0x2D2D2D, string.format("%02X" , bytes[selection.from]), "Type byte value")) input.onInputFinished = function(text) @@ -110,15 +118,15 @@ local function byteFieldEventHandler(application, object, e1, e2, e3, e4, e5) end container:remove() - application:draw() + workspace:draw() end end - application:draw() + workspace:draw() end menu:addItem("Insert").onTouch = function() - local container = MineOSInterface.addBackgroundContainer(application, "Insert bytes at position " .. selection.from .. "") + local container = system.addBackgroundContainer(workspace, "Insert bytes at position " .. selection.from .. "") local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x666666, 0x666666, 0xE1E1E1, 0x2D2D2D, "", "Type byte values separated by space", true)) local switch = container.layout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0x1E1E1E, 0xE1E1E1, 0xBBBBBB, "Select inserted bytes:", true)).switch @@ -138,11 +146,11 @@ local function byteFieldEventHandler(application, object, e1, e2, e3, e4, e5) end container:remove() - application:draw() + workspace:draw() end end - application:draw() + workspace:draw() end menu:addSeparator() @@ -158,7 +166,7 @@ local function byteFieldEventHandler(application, object, e1, e2, e3, e4, e5) end end - application:draw() + workspace:draw() else local index = (math.ceil((e4 - object.y + 1) / 2) - 1) * 16 + math.ceil((e3 - object.x + 1 + object.offset) / object.elementWidth) + offset @@ -180,7 +188,7 @@ local function byteFieldEventHandler(application, object, e1, e2, e3, e4, e5) end status() - application:draw() + workspace:draw() end end elseif e1 == "scroll" then @@ -192,7 +200,7 @@ local function byteFieldEventHandler(application, object, e1, e2, e3, e4, e5) end scrollBar.value = offset - application:draw() + workspace:draw() end end @@ -218,7 +226,7 @@ local charField = window:addChild(newByteField(byteField.localX + byteField.widt local separator = window:addChild(GUI.object(byteField.localX + byteField.width, 5, 1, 21)) separator.draw = function(object) for i = object.y, object.y + object.height - 1 do - buffer.drawText(object.x, i, colors.separator, "│") + screen.drawText(object.x, i, colors.separator, "│") end end @@ -228,23 +236,23 @@ window:addChild(GUI.panel(11, 4, window.width - 10, 1, colors.panel)) -- Vertical local verticalCounter = window:addChild(GUI.object(1, 4, 10, window.height - 3)) verticalCounter.draw = function(object) - buffer.drawRectangle(object.x, object.y, object.width, object.height, colors.panel, colors.panelText, " ") + screen.drawRectangle(object.x, object.y, object.width, object.height, colors.panel, colors.panelText, " ") local index = offset for y = 2, object.height - 1, 2 do local textColor = colors.panelText if index > selection.from and index < selection.to then - buffer.drawRectangle(object.x, object.y + y - 1, object.width, 2, colors.panelSeleciton, colors.panelSelecitonText, " ") + screen.drawRectangle(object.x, object.y + y - 1, object.width, 2, colors.panelSeleciton, colors.panelSelecitonText, " ") textColor = colors.panelSelecitonText end if selection.from >= index and selection.from <= index + 15 or selection.to >= index and selection.to <= index + 15 then - buffer.drawRectangle(object.x, object.y + y, object.width, 1, colors.selectionFrom, colors.selectionText, " ") + screen.drawRectangle(object.x, object.y + y, object.width, 1, colors.selectionFrom, colors.selectionText, " ") textColor = colors.selectionText end - buffer.drawText(object.x + 1, object.y + y, textColor, string.format("%08X", index)) + screen.drawText(object.x + 1, object.y + y, textColor, string.format("%08X", index)) index = index + 16 end @@ -257,14 +265,14 @@ window:addChild(GUI.object(13, 4, 62, 1)).draw = function(object) for x = 1, object.width, 4 do local textColor = colors.panelText if counter + 1 > restFrom and counter + 1 < restTo then - buffer.drawRectangle(object.x + x - 2, object.y, 4, 1, colors.panelSeleciton, colors.selectionText, " ") + screen.drawRectangle(object.x + x - 2, object.y, 4, 1, colors.panelSeleciton, colors.selectionText, " ") textColor = colors.panelSelecitonText elseif restFrom == counter + 1 or restTo == counter + 1 then - buffer.drawRectangle(object.x + x - 2, object.y, 4, 1, colors.selectionFrom, colors.selectionText, " ") + screen.drawRectangle(object.x + x - 2, object.y, 4, 1, colors.selectionFrom, colors.selectionText, " ") textColor = colors.selectionText end - buffer.drawText(object.x + x - 1, object.y, textColor, string.format("%02X", counter)) + screen.drawText(object.x + x - 1, object.y, textColor, string.format("%02X", counter)) counter = counter + 1 end end @@ -295,7 +303,7 @@ local openFileButton = window:addChild(GUI.adaptiveRoundedButton(saveFileButton. ------------------------------------------------------------------------------------------------------------------ local function load(path) - local file, reason = io.open(path, "rb") + local file, reason = filesystem.open(path, "rb") if file then bytes = {} @@ -320,21 +328,21 @@ local function load(path) end openFileButton.onTouch = function() - local filesystemDialog = GUI.addFilesystemDialog(application, true, 50, math.floor(application.height * 0.8), "Open", "Cancel", "File name", "/") + local filesystemDialog = GUI.addFilesystemDialog(workspace, true, 50, math.floor(workspace.height * 0.8), "Open", "Cancel", "File name", "/") filesystemDialog:setMode(GUI.IO_MODE_OPEN, GUI.IO_MODE_FILE) filesystemDialog:show() filesystemDialog.onSubmit = function(path) load(path) - application:draw() + workspace:draw() end end saveFileButton.onTouch = function() - local filesystemDialog = GUI.addFilesystemDialog(application, true, 50, math.floor(application.height * 0.8), "Save", "Cancel", "File name", "/") + local filesystemDialog = GUI.addFilesystemDialog(workspace, true, 50, math.floor(workspace.height * 0.8), "Save", "Cancel", "File name", "/") filesystemDialog:setMode(GUI.IO_MODE_SAVE, GUI.IO_MODE_FILE) filesystemDialog:show() filesystemDialog.onSubmit = function(path) - local file = io.open(path, "wb") + local file = filesystem.open(path, "wb") if file then for i = 1, #bytes do file:write(string.char(bytes[i])) @@ -358,13 +366,13 @@ window.actionButtons.maximize.onTouch = function() window.localY = 1 - application:draw() + workspace:draw() end ------------------------------------------------------------------------------------------------------------------ -load("/bin/resolution.lua") -application:draw() +load("/OS.lua") +workspace:draw() diff --git a/Applications/HEX/About/English.txt b/Applications/HEX/About/English.txt deleted file mode 100644 index 06f490e2..00000000 --- a/Applications/HEX/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -HEX - мощный редактор файлов в шестнадцатеричном режиме. Он позволяет индивидуально редактировать байты, удалять их, инвертировать, вставлять новые. Незаменимая вещь для тру прогеров! \ No newline at end of file diff --git a/Applications/HEX/About/Russian.txt b/Applications/HEX/About/Russian.txt deleted file mode 100644 index 06f490e2..00000000 --- a/Applications/HEX/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -HEX - мощный редактор файлов в шестнадцатеричном режиме. Он позволяет индивидуально редактировать байты, удалять их, инвертировать, вставлять новые. Незаменимая вещь для тру прогеров! \ No newline at end of file diff --git a/Applications/Highlight/Highlight.lua b/Applications/Highlight/Highlight.lua deleted file mode 100644 index 6130aa84..00000000 --- a/Applications/Highlight/Highlight.lua +++ /dev/null @@ -1,13 +0,0 @@ -local syntax = require("syntax") -ecs.prepareToExit() -local data = ecs.universalWindow("auto", "auto", 40, 0xeeeeee, true, {"EmptyLine"}, {"CenterText", 0x262626, "Enter path to file"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "MineOS/Applications/Highlight.app/Resources/TestFile.txt"}, {"EmptyLine"}, {"Button", {0x33db80, 0xffffff, "GO!"}}) -local path = data[1] - -local strings, maxStringWidth = syntax.convertFileToStrings(path) -local xSize, ySize = gpu.getResolution() -buffer.square(1, 1, xSize, ySize, ecs.colors.green, 0xFFFFFF, " ") -buffer.draw(true) -syntax.viewCode(2, 2, 70, 20, strings, maxStringWidth, 1, 1, true, {from = {x = 6, y = 2}, to = {x = 10, y = 4}}) -buffer.draw() - -ecs.waitForTouchOrClick() diff --git a/Applications/Highlight/Icon.pic b/Applications/Highlight/Icon.pic deleted file mode 100644 index c5e3a622e9f27f4ea03a18f6f412072ce08d0223..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 313 zcmYL^I|>3p5Jb0XdOoQ)5F=eM&>OhIfRRC)Oem-&SLx;W)pkkBp9T618J>xfY>29FS4gO$ZbhBd1uj7}5pBYLbkK zrBQ9X8P%BcL@p}lt&NCjG_KY$6*{nZ5g%ewoZ1-Rln*0sY4xaWI|}rQx4HW1%P1+I I77 0.33 then config.holoScale = config.holoScale - 0.1; component.hologram.setScale(config.holoScale); save() end + if config.holoScale > 0.33 then config.holoScale = config.holoScale - 0.1; hologram.setScale(config.holoScale); save() end end elseif e[1] == "key_down" then if e[4] == 19 then @@ -237,7 +236,7 @@ while true do save() elseif e[4] == 28 then save() - component.hologram.clear() + hologram.clear() return end end diff --git a/Applications/HoloEdit/HoloEdit.lua b/Applications/HoloEdit/HoloEdit.lua deleted file mode 100644 index ee7cc7a9..00000000 --- a/Applications/HoloEdit/HoloEdit.lua +++ /dev/null @@ -1,785 +0,0 @@ --- Hologram Editor --- by NEO, Totoro --- 10/14/2014, all right reserved =) --- райтс, хуяйтс, резервед ЙОПТА - -local MineOSCore = require("MineOSCore") -local unicode = require('unicode') -local event = require('event') -local term = require('term') -local fs = require('filesystem') -local com = require('component') -local gpu = com.gpu - -local lang = MineOSCore.getCurrentScriptLocalization() - --- Константы -- -HOLOH = 32 -HOLOW = 48 - --- Цвета -- -backcolor = 0x000000 -forecolor = 0xFFFFFF -infocolor = 0x0066FF -errorcolor = 0xFF0000 -helpcolor = 0x006600 -graycolor = 0x080808 -goldcolor = 0xFFDF00 --- *** -- - - --- загружаем доп. оборудование -function trytofind(name) - if com.isAvailable(name) then - return com.getPrimary(name) - else - return nil - end -end - -local h = trytofind('hologram') - --- ========================================= H O L O G R A P H I C S ========================================= -- -holo = {} -function set(x, y, z, value) - if holo[x] == nil then holo[x] = {} end - if holo[x][y] == nil then holo[x][y] = {} end - holo[x][y][z] = value -end -function get(x, y, z) - if holo[x] ~= nil and holo[x][y] ~= nil and holo[x][y][z] ~= nil then - return holo[x][y][z] - else - return 0 - end -end - -function save(filename) - -- сохраняем палитру - file = io.open(filename, 'wb') - for i=1, 3 do - for c=1, 3 do - file:write(string.char(colortable[i][c])) - end - end - -- сохраняем массив - for x=1, HOLOW do - for y=1, HOLOH do - for z=1, HOLOW, 4 do - a = get(x,y,z) - b = get(x,y,z+1) - c = get(x,y,z+2) - d = get(x,y,z+3) - byte = d*64 + c*16 + b*4 + a - file:write(string.char(byte)) - end - end - end - file:close() -end - -local function load(filename) - if fs.exists(filename) then - file = io.open(filename, 'rb') - -- загружаем палитру - for i=1, 3 do - for c=1, 3 do - colortable[i][c] = string.byte(file:read(1)) - end - setHexColor(i,colortable[i][1], - colortable[i][2], - colortable[i][3]) - end - -- загружаем массив - holo = {} - for x=1, HOLOW do - for y=1, HOLOH do - for z=1, HOLOW, 4 do - byte = string.byte(file:read(1)) - for i=0, 3 do - a = byte % 4 - byte = math.floor(byte / 4) - if a ~= 0 then set(x,y,z+i, a) end - end - end - end - end - file:close() - return true - else - --print("[ОШИБКА] Файл "..filename.." не найден.") - return false - end -end - - --- ============================================= G R A P H I C S ============================================= -- --- проверка разрешения экрана, для комфортной работы необходимо разрешение > HOLOW по высоте и ширине -OLDWIDTH, OLDHEIGHT = gpu.getResolution() -WIDTH, HEIGHT = gpu.maxResolution() -if HEIGHT < HOLOW+2 then - error(lang.badGPU) -else - WIDTH = HOLOW*2+40 - HEIGHT = HOLOW+2 - gpu.setResolution(WIDTH, HEIGHT) -end -gpu.setForeground(forecolor) -gpu.setBackground(backcolor) - --- рисуем линию -local strLine = "+" -for i=1, WIDTH do - strLine = strLine..'-' -end -function line(x1, x2, y) - gpu.set(x1,y,string.sub(strLine, 1, x2-x1)) - gpu.set(x2,y,'+') -end - --- рисуем фрейм -function frame(x1, y1, x2, y2, caption) - line(x1, x2, y1) - line(x1, x2, y2) - - if caption ~= nil then - gpu.set(x1+(x2-x1)/2-unicode.len(caption)/2, y1, caption) - end -end - --- рисуем сетку -local strGrid = "" -for i=1, HOLOW/2 do - strGrid = strGrid.."██ " -end -function drawGrid(x, y) - gpu.fill(0, y, MENUX, HOLOW, ' ') - gpu.setForeground(graycolor) - for i=0, HOLOW-1 do - if view>0 and i==HOLOH then - gpu.setForeground(forecolor) - line(1, MENUX-1, y+HOLOH) - break - end - gpu.set(x+(i%2)*2, y+i, strGrid) - end - if view == 0 then gpu.setForeground(forecolor) end -end - --- рисуем цветной прямоугольник -function drawRect(x, y, color) - gpu.set(x, y, "╓──────╖") - gpu.set(x, y+1, "║ ║") - gpu.set(x, y+2, "╙──────╜") - gpu.setForeground(color) - gpu.set(x+2, y+1, "████") - gpu.setForeground(forecolor) -end - -MENUX = HOLOW*2+5 -BUTTONW = 12 - --- рисуем меню выбора "кисти" -function drawColorSelector() - frame(MENUX, 3, WIDTH-2, 16, lang.palette) - for i=0, 3 do - drawRect(MENUX+1+i*8, 5, hexcolortable[i]) - end - gpu.set(MENUX+1, 10, "R:") - gpu.set(MENUX+1, 11, "G:") - gpu.set(MENUX+1, 12, "B:") -end -function drawColorCursor(force) - if brush.color*8 ~= brush.x then brush.x = brush.color*8 end - if force or brush.gx ~= brush.x then - gpu.set(MENUX+1+brush.gx, 8, " ") - if brush.gx < brush.x then brush.gx = brush.gx + 1 end - if brush.gx > brush.x then brush.gx = brush.gx - 1 end - gpu.set(MENUX+1+brush.gx, 8, " -^--^- ") - end -end -function drawLayerSelector() - frame(MENUX, 16, WIDTH-2, 28, lang.layer) - gpu.set(MENUX+13, 18, lang.level) - gpu.set(MENUX+1, 23, lang.mainLevel) -end -function drawButtonsPanel() - frame(MENUX, 28, WIDTH-2, 36, lang.control) -end - -function mainScreen() - term.clear() - frame(1,1, WIDTH, HEIGHT, "{ Hologram Editor }") - -- "холст" - drawLayer() - drawColorSelector() - drawColorCursor(true) - drawLayerSelector() - drawButtonsPanel() - buttonsDraw() - textboxesDraw() - -- "about" - коротко о создателях - gpu.setForeground(infocolor) - gpu.setBackground(graycolor) - gpu.set(MENUX+3, HEIGHT-11, " Hologram Editor v0.60 Beta ") - gpu.setForeground(forecolor) - gpu.set(MENUX+3, HEIGHT-10, " * * * ") - gpu.set(MENUX+3, HEIGHT-9, lang.developers) - gpu.set(MENUX+3, HEIGHT-8, " NEO, Totoro ") - gpu.set(MENUX+3, HEIGHT-7, " * * * ") - gpu.set(MENUX+3, HEIGHT-6, lang.contact) - gpu.set(MENUX+3, HEIGHT-5, " computercraft.ru/forum ") - gpu.setBackground(backcolor) - -- выход - gpu.set(MENUX, HEIGHT-2, lang.quit) -end - - --- =============================================== L A Y E R S =============================================== -- -GRIDX = 3 -GRIDY = 2 -function drawLayer() - drawGrid(GRIDX, GRIDY) - -- вид сверху (y) - if view == 0 then - for x=1, HOLOW do - for z=1, HOLOW do - gn = get(x, ghost_layer, z) - n = get(x, layer, z) - if n == 0 and gn ~= 0 then - gpu.setForeground(darkhexcolors[gn]) - gpu.set((GRIDX-2) + x*2, (GRIDY-1) + z, "░░") - end - if n ~= 0 then - gpu.setForeground(hexcolortable[n]) - gpu.set((GRIDX-2) + x*2, (GRIDY-1) + z, "██") - end - end - end - -- вид спереди (z) - elseif view == 1 then - for x=1, HOLOW do - for y=1, HOLOH do - n = get(x, y, layer) - gn = get(x, y, ghost_layer) - if n == 0 and gn ~= 0 then - gpu.setForeground(darkhexcolors[gn]) - gpu.set((GRIDX-2) + x*2, (GRIDY+HOLOH) - y, "░░") - end - if n ~= 0 then - gpu.setForeground(hexcolortable[n]) - gpu.set((GRIDX-2) + x*2, (GRIDY+HOLOH) - y, "██") - end - end - end - -- вид сбоку (x) - else - for z=1, HOLOW do - for y=1, HOLOH do - gn = get(ghost_layer, y, z) - n = get(layer, y, z) - if n == 0 and gn ~= 0 then - gpu.setForeground(darkhexcolors[gn]) - gpu.set((GRIDX+HOLOW*2) - z*2, (GRIDY+HOLOH) - y, "░░") - end - if n ~= 0 then - gpu.setForeground(hexcolortable[n]) - gpu.set((GRIDX+HOLOW*2) - z*2, (GRIDY+HOLOH) - y, "██") - end - end - end - end - gpu.setForeground(forecolor) - -- for messages - repaint = false -end -function fillLayer() - for x=1, HOLOW do - for z=1, HOLOW do - set(x, layer, z, brush.color) - end - end - drawLayer() -end -function clearLayer() - for x=1, HOLOW do - if holo[x] ~= nil then holo[x][layer] = nil end - end - drawLayer() -end - - --- ============================================== B U T T O N S ============================================== -- -Button = {} -Button.__index = Button -function Button.new(func, x, y, text, color, width) - self = setmetatable({}, Button) - - self.form = '[ ' - if width == nil then width = 0 - else width = (width - unicode.len(text))-4 end - for i=1, math.floor(width/2) do - self.form = self.form.. ' ' - end - self.form = self.form..text - for i=1, math.ceil(width/2) do - self.form = self.form.. ' ' - end - self.form = self.form..' ]' - - self.func = func - - self.x = x; self.y = y - self.color = color - self.visible = true - - return self -end -function Button:draw(color) - if self.visible then - local color = color or self.color - gpu.setBackground(color) - if color > 0x888888 then gpu.setForeground(backcolor) end - gpu.set(self.x, self.y, self.form) - gpu.setBackground(backcolor) - if color > 0x888888 then gpu.setForeground(forecolor) end - end -end -function Button:click(x, y) - if self.visible then - if y == self.y then - if x >= self.x and x < self.x+unicode.len(self.form) then - self.func() - self:draw(self.color/2) - os.sleep(0.1) - self:draw() - return true - end - end - end - return false -end -buttons = {} -function buttonsNew(func, x, y, text, color, width) - table.insert(buttons, Button.new(func, x, y, text, color, width)) -end -function buttonsDraw() - for i=1, #buttons do - buttons[i]:draw() - end -end -function buttonsClick(x, y) - for i=1, #buttons do - buttons[i]:click(x, y) - end -end - --- ================================ B U T T O N S F U N C T I O N A L I T Y ================================ -- -function exit() running = false end -function nextLayer() - -- ограничения разные для разных видов/проекций - local limit = HOLOH - if view > 0 then limit = HOLOW end - - if layer < limit then - layer = layer + 1 - tb_layer:setValue(layer) - tb_layer:draw(true) - moveGhost() - drawLayer() - end -end -function prevLayer() - if layer > 1 then - layer = layer - 1 - tb_layer:setValue(layer) - tb_layer:draw(true) - moveGhost() - drawLayer() - end -end -function setLayer(value) - local n = tonumber(value) - local limit = HOLOH - if view > 0 then limit = HOLOW end - if n == nil or n < 1 or n > limit then return false end - layer = n - moveGhost() - drawLayer() - return true -end -function nextGhost() - local limit = HOLOH - if view > 0 then limit = HOLOW end - - if ghost_layer_below then - ghost_layer_below = false - if ghost_layer < limit then - ghost_layer = layer + 1 - else ghost_layer = limit end - drawLayer() - else - if ghost_layer < limit then - ghost_layer = ghost_layer + 1 - drawLayer() - end - end -end -function prevGhost() - if not ghost_layer_below then - ghost_layer_below = true - if layer > 1 then - ghost_layer = layer - 1 - else ghost_layer = 1 end - drawLayer() - else - if ghost_layer > 1 then - ghost_layer = ghost_layer - 1 - drawLayer() - end - end -end -function setGhostLayer(value) - local n = tonumber(value) - local limit = HOLOH - if view > 0 then limit = HOLOW end - if n == nil or n < 1 or n > limit then return false end - ghost_layer = n - drawLayer() - return true -end -function moveGhost() - if ghost_layer_below then - if layer > 1 then ghost_layer = layer - 1 - else ghost_layer = 1 end - else - local limit = HOLOH - if view > 0 then limit = HOLOW end - if layer < limit then ghost_layer = layer + 1 - else ghost_layer = limit end - end -end - -function setFilename(str) - if str ~= nil and str ~= '' and unicode.len(str)<30 then - return true - else - return false - end -end - -function setHexColor(n, r, g, b) - local hexcolor = rgb2hex(r,g,b) - hexcolortable[n] = hexcolor - darkhexcolors[n] = bit32.rshift(bit32.band(hexcolor, 0xfefefe), 1) -end -function rgb2hex(r,g,b) - return r*65536+g*256+b -end -function changeRed(value) return changeColor(1, value) end -function changeGreen(value) return changeColor(2, value) end -function changeBlue(value) return changeColor(3, value) end -function changeColor(rgb, value) - if value == nil then return false end - n = tonumber(value) - if n == nil or n < 0 or n > 255 then return false end - -- сохраняем данные в таблицу - colortable[brush.color][rgb] = n - setHexColor(brush.color, colortable[brush.color][1], - colortable[brush.color][2], - colortable[brush.color][3]) - -- обновляем цвета на панельке - for i=0, 3 do - drawRect(MENUX+1+i*8, 5, hexcolortable[i]) - end - return true -end - -function moveSelector(num) - brush.color = num - tb_red:setValue(colortable[num][1]); tb_red:draw(true) - tb_green:setValue(colortable[num][2]); tb_green:draw(true) - tb_blue:setValue(colortable[num][3]); tb_blue:draw(true) -end - -function setTopView() - view = 0 - -- в виде сверху меньше слоев - if layer > HOLOH then layer = HOLOH end - drawLayer() -end -function setFrontView() view = 1; drawLayer() end -function setSideView() view = 2; drawLayer() end - -function drawHologram() - -- проверка на наличие проектора - h = trytofind('hologram') - if h ~= nil then - local depth = h.maxDepth() - -- очищаем - h.clear() - -- отправляем палитру - if depth == 2 then - for i=1, 3 do - h.setPaletteColor(i, hexcolortable[i]) - end - else - h.setPaletteColor(1, hexcolortable[1]) - end - -- отправляем массив - for x=1, HOLOW do - for y=1, HOLOH do - for z=1, HOLOW do - n = get(x,y,z) - if n ~= 0 then - if depth == 2 then - h.set(x,y,z,n) - else - h.set(x,y,z,1) - end - end - end - end - end - end -end - -function newHologram() - holo = {} - drawLayer() -end - -function saveHologram() - local filename = tb_file:getValue() - if filename ~= FILE_REQUEST then - -- выводим предупреждение - showMessage(lang.savingFile, lang.attention, goldcolor) - -- добавляем фирменное расширение =) - if string.sub(filename, -3) ~= '.3d' then - filename = filename..'.3d' - end - -- сохраняем - save(filename) - -- выводим предупреждение - showMessage(lang.complete, lang.attention, goldcolor) - repaint = true - end -end - -function loadHologram() - local filename = tb_file:getValue() - if filename ~= FILE_REQUEST then - -- выводим предупреждение - showMessage(lang.loadingFile, lang.attention, goldcolor) - -- добавляем фирменное расширение =) - if string.sub(filename, -3) ~= '.3d' then - filename = filename..'.3d' - end - -- загружаем - load(filename) - -- обновляем значения в текстбоксах - tb_red:setValue(colortable[brush.color][1]); tb_red:draw(true) - tb_green:setValue(colortable[brush.color][2]); tb_green:draw(true) - tb_blue:setValue(colortable[brush.color][3]); tb_blue:draw(true) - -- обновляем цвета на панельке - for i=0, 3 do - drawRect(MENUX+1+i*8, 5, hexcolortable[i]) - end - -- обновляем слой - drawLayer() - end -end - --- ============================================ T E X T B O X E S ============================================ -- -Textbox = {} -Textbox.__index = Textbox -function Textbox.new(func, x, y, value, width) - self = setmetatable({}, Textbox) - - self.form = '>' - if width == nil then width = 10 end - for i=1, width-1 do - self.form = self.form..' ' - end - - self.func = func - self.value = tostring(value) - - self.x = x; self.y = y - self.visible = true - - return self -end -function Textbox:draw(content) - if self.visible then - if content then gpu.setBackground(graycolor) end - gpu.set(self.x, self.y, self.form) - if content then gpu.set(self.x+2, self.y, self.value) end - gpu.setBackground(backcolor) - end -end -function Textbox:click(x, y) - if self.visible then - if y == self.y then - if x >= self.x and x < self.x+unicode.len(self.form) then - self:draw(false) - term.setCursor(self.x+2, self.y) - value = string.sub(term.read({self.value}), 1, -2) - if self.func(value) then - self.value = value - end - self:draw(true) - return true - end - end - end - return false -end -function Textbox:setValue(value) - self.value = tostring(value) -end -function Textbox:getValue() - return self.value -end -textboxes = {} -function textboxesNew(func, x, y, value, width) - textbox = Textbox.new(func, x, y, value, width) - table.insert(textboxes, textbox) - return textbox -end -function textboxesDraw() - for i=1, #textboxes do - textboxes[i]:draw(true) - end -end -function textboxesClick(x, y) - for i=1, #textboxes do - textboxes[i]:click(x, y) - end -end - - --- ============================================= M E S S A G E S ============================================= -- -repaint = false -function showMessage(text, caption, color) - local x = WIDTH/2 - unicode.len(text)/2 - 4 - local y = HEIGHT/2 - 2 - gpu.fill(x, y, unicode.len(text)+8, 5, ' ') - frame(x, y, x+unicode.len(text)+7, y+4, caption) - gpu.setForeground(color) - gpu.set(x+4,y+2, text) - gpu.setForeground(forecolor) -end - - --- =========================================== M A I N C Y C L E =========================================== -- --- инициализация -colortable = {{255, 0, 0}, {0, 255, 0}, {0, 102, 255}} -colortable[0] = {0, 0, 0} -hexcolortable = {} -darkhexcolors = {} -for i=0,3 do setHexColor(i, colortable[i][1], colortable[i][2], colortable[i][3]) end -brush = {color = 1, x = 8, gx = 8} -ghost_layer = 1 -ghost_layer_below = true -layer = 1 -view = 0 -running = true - -buttonsNew(exit, WIDTH-BUTTONW-2, HEIGHT-2, lang.exit, errorcolor, BUTTONW) -buttonsNew(drawLayer, MENUX+10, 14, lang.refresh, goldcolor, BUTTONW) -buttonsNew(prevLayer, MENUX+1, 19, '-', infocolor, 5) -buttonsNew(nextLayer, MENUX+7, 19, '+', infocolor, 5) -buttonsNew(setTopView, MENUX+1, 21, lang.fromUp, infocolor, 10) -buttonsNew(setFrontView, MENUX+12, 21, lang.fromFront, infocolor, 10) -buttonsNew(setSideView, MENUX+24, 21, lang.fromSide, infocolor, 9) - -buttonsNew(prevGhost, MENUX+1, 24, lang.lower, infocolor, 6) -buttonsNew(nextGhost, MENUX+10, 24, lang.upper, infocolor, 6) - -buttonsNew(clearLayer, MENUX+1, 26, lang.clear, infocolor, BUTTONW) -buttonsNew(fillLayer, MENUX+2+BUTTONW, 26, lang.fill, infocolor, BUTTONW) - -buttonsNew(drawHologram, MENUX+8, 30, lang.toProjector, goldcolor, 16) -buttonsNew(saveHologram, MENUX+1, 33, lang.save, helpcolor, BUTTONW) -buttonsNew(loadHologram, MENUX+8+BUTTONW, 33, lang.load, infocolor, BUTTONW) -buttonsNew(newHologram, MENUX+1, 35, lang.new, infocolor, BUTTONW) - -tb_red = textboxesNew(changeRed, MENUX+5, 10, '255', WIDTH-MENUX-7) -tb_green = textboxesNew(changeGreen, MENUX+5, 11, '0', WIDTH-MENUX-7) -tb_blue = textboxesNew(changeBlue, MENUX+5, 12, '0', WIDTH-MENUX-7) -tb_layer = textboxesNew(setLayer, MENUX+13, 19, '1', WIDTH-MENUX-15) -tb_ghostlayer = textboxesNew(setGhostLayer, MENUX+19, 24, ' ', WIDTH-MENUX-21) -FILE_REQUEST = lang.enterFileName -tb_file = textboxesNew(setFilename, MENUX+1, 32, FILE_REQUEST, WIDTH-MENUX-3) -mainScreen() - -while running do - if brush.x ~= brush.gx then name, add, x, y, b = event.pull(0.02) - else name, add, x, y, b = event.pull(1.0) end - - if name == 'key_down' then - -- если нажата 'Q' - выходим - if y == 16 then break - elseif y == 41 then - moveSelector(0) - elseif y>=2 and y<=4 then - moveSelector(y-1) - elseif y == 211 then - clearLayer() - end - elseif name == 'touch' then - -- проверка GUI - buttonsClick(x, y) - textboxesClick(x, y) - -- выбор цвета - if x>MENUX+1 and x4 and y<8 then - moveSelector(math.floor((x-MENUX-1)/8)) - end - end - end - if name == 'touch' or name == 'drag' then - -- "рисование" - local limit = HOLOW - if view > 0 then limit = HOLOH end - if x >= GRIDX and x < GRIDX+HOLOW*2 then - if y >= GRIDY and y < GRIDY+limit then - -- перерисуем, если на экране был мессейдж - if repaint then drawLayer() end - -- рассчет клика - if view == 0 then - dx = math.floor((x-GRIDX)/2)+1; gx = dx - dy = layer; gy = ghost_layer - dz = y-GRIDY+1; gz = dz - elseif view == 1 then - dx = math.floor((x-GRIDX)/2)+1; gx = dx - dy = HOLOH - (y-GRIDY); gy = dy - dz = layer; gz = ghost_layer - else - dx = layer; gx = ghost_layer - dy = HOLOH - (y-GRIDY); gy = dy - dz = HOLOW - math.floor((x-GRIDX)/2); gz = dz - end - if b == 0 and brush.color ~= 0 then - set(dx, dy, dz, brush.color) - gpu.setForeground(hexcolortable[brush.color]) - gpu.set(x-(x-GRIDX)%2, y, "██") - else - set(dx, dy, dz, 0) - gpu.setForeground(darkhexcolors[get(gx,gy,gz)]) - gpu.set(x-(x-GRIDX)%2, y, "░░") - end - gpu.setForeground(forecolor) - end - end - end - - drawColorCursor() -end - --- завершение -term.clear() -gpu.setResolution(OLDWIDTH, OLDHEIGHT) -gpu.setForeground(0xFFFFFF) -gpu.setBackground(0x000000) diff --git a/Applications/HoloEdit/Icon.pic b/Applications/HoloEdit/Icon.pic deleted file mode 100644 index 1e74fc67118c596b853a792f88e0c76e0901c256..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 86 zcmXZR!3lss3`EiVNzA4Udl3Xr)?`2SxQ4~k@c!J#b!LU8v13>uByc*8vd;cuQp#h? R;f$wAE|sX+^km)f?*}I|1%3bk diff --git a/Applications/HoloEdit/Localizations/English.lang b/Applications/HoloEdit/Localizations/English.lang deleted file mode 100644 index 1cff576d..00000000 --- a/Applications/HoloEdit/Localizations/English.lang +++ /dev/null @@ -1,29 +0,0 @@ -{ - badGPU = "[ERROR] Your monitor/GPU doesn't support required resolution.", - palette = "[ Palette ]", - control = "[ Control ]", - layer = "[ Layer ]", - level = "Hologram level:", - mainLevel = "Main level:", - quit = "Exit: 'Q' or ", - developers = "Developers: ", - contact = "Contact", - savingFile = "Saving file...", - loadingFile = "Loading file...", - attention = "[ Attention ]", - complete = "[ Done! ]", - exit = "Quit", - refresh = "Refresh", - fromUp = "Top", - fromFront = "Front", - fromSide = "Side", - upper = "Upper", - lower = "Lower", - clear = "Clear", - fill = "Fill", - toProjector = "To projector", - save = "Save", - load = "Load", - new = "New file", - enterFileName = "Enter file name here:", -} \ No newline at end of file diff --git a/Applications/HoloEdit/Localizations/French.lang b/Applications/HoloEdit/Localizations/French.lang deleted file mode 100644 index a8223cf5..00000000 --- a/Applications/HoloEdit/Localizations/French.lang +++ /dev/null @@ -1,29 +0,0 @@ -{ - badGPU = "[ERREUR] Votre moniteur / GPU ne prend pas en charge la résolution requise.", - palette = "[ Palette ]", - control = "[ Controle ]", - layer = "[ Couche ]", - level = "Niveau d'hologramme :", - mainLevel = "Niveau principal :", - quit = "Quitter: 'Q' ou ", - developers = "Développeurs : ", - contact = "Contact", - savingFile = "Enregistrement du fichier ...", - loadingFile = "Chargement du fichier ...", - attention = "[ Attention ]", - complete = "[ Terminé ! ]", - exit = "Quitter", - refresh = "Rafraîchir", - fromUp = "Haut", - fromFront = "Devant", - fromSide = "Côté", - upper = "monter", - lower = "descendre", - clear = "Effacer", - fill = "Remplir", - toProjector = "Vers le projecteur", - save = "Enregistrer", - load = "Charger", - new = "Nouveau fichier", - enterFileName = "Entrez le nom du fichier :", -} \ No newline at end of file diff --git a/Applications/HoloEdit/Localizations/Russian.lang b/Applications/HoloEdit/Localizations/Russian.lang deleted file mode 100644 index 77a3d31f..00000000 --- a/Applications/HoloEdit/Localizations/Russian.lang +++ /dev/null @@ -1,29 +0,0 @@ -{ - badGPU = "[ОШИБКА] Ваш монитор/видеокарта не поддерживает требуемое разрешение.", - palette = "[ Палитра ]", - control = "[ Управление ]", - layer = "[ Слой ]", - level = "Уровень голограммы:", - mainLevel = "Направляющий уровень:", - quit = "Выход: 'Q' или ", - developers = "Разработчики: ", - contact = "Контакты:", - savingFile = "Сохраняю файл...", - loadingFile = "Загружаю файл...", - attention = "[ Внимание ]", - complete = "[ Файл сохранен! ]", - exit = "Выход", - refresh = "Обновить", - fromUp = "Сверху", - fromFront = "Спереди", - fromSide = "Сбоку", - upper = "Выше", - lower = "Ниже", - clear = "Очистить", - fill = "Залить", - toProjector = "На проектор", - save = "Сохранить", - load = "Загрузить", - new = "Новый файл", - enterFileName = "Введите сюда имя файла", -} \ No newline at end of file diff --git a/Applications/IRC/Icon.pic b/Applications/IRC.app/Icon.pic similarity index 100% rename from Applications/IRC/Icon.pic rename to Applications/IRC.app/Icon.pic diff --git a/Applications/IRC/Main.lua b/Applications/IRC.app/Main.lua similarity index 91% rename from Applications/IRC/Main.lua rename to Applications/IRC.app/Main.lua index 105566cf..e7626156 100644 --- a/Applications/IRC/Main.lua +++ b/Applications/IRC.app/Main.lua @@ -1,13 +1,13 @@ -local component = require("component") -local computer = require("computer") -local buffer = require("doubleBuffering") + +local screen = require("Screen") local GUI = require("GUI") -local color = require("color") -local filesystem = require("filesystem") -local unicode = require("unicode") -local MineOSCore = require("MineOSCore") -local MineOSPaths = require("MineOSPaths") -local MineOSInterface = require("MineOSInterface") +local color = require("Color") +local filesystem = require("Filesystem") +local system = require("System") +local paths = require("Paths") +local system = require("System") +local text = require("Text") +local number = require("Number") ------------------------------------------------------------------------------- @@ -46,7 +46,7 @@ local colorScheme = { channelDataMessageText = 0xB4B4B4, } -local applicationPath = MineOSPaths.applicationData .. "IRC/" +local applicationPath = paths.user.applicationData .. "IRC/" local configPath = applicationPath .. "Config.cfg" local historyPath = applicationPath .. "History.cfg" @@ -67,16 +67,16 @@ local history = { } if filesystem.exists(configPath) then - config = table.fromFile(configPath) + config = filesystem.readTable(configPath) end if filesystem.exists(historyPath) then - history = table.fromFile(historyPath) + history = filesystem.readTable(historyPath) end ------------------------------------------------------------------------------- -local application, window = MineOSInterface.addWindow(GUI.filledWindow(1, 1, 110, 27, 0xE1E1E1)) +local workspace, window = system.addWindow(GUI.filledWindow(1, 1, 110, 27, 0xE1E1E1)) local leftPanel = window:addChild(GUI.panel(1, 1, 21, 1, 0x2D2D2D)) local leftLayout = window:addChild(GUI.layout(1, 4, leftPanel.width, 1, 1, 1)) @@ -161,7 +161,7 @@ local function getItemByText(name) end local function saveConfig() - table.toFile(configPath, config) + filesystem.writeTable(configPath, config) end local function socketWrite(data) @@ -187,7 +187,7 @@ local function status(text) loginStatusText.hidden, loginServerLayout.hidden, loginUsernameInput.hidden, loginPasswordSwitchAndLabel.hidden, loginSubmitButton.hidden = not state, state, state, state, state loginPasswordInput.hidden = state and true or not loginPasswordSwitchAndLabel.switch.state - application:draw() + workspace:draw() end local function updateLeftLayout() @@ -206,22 +206,22 @@ local function contactItemDraw(item) local background = item.pressed and item.colors.pressed.background or item.colors.default.background local foreground = item.pressed and item.colors.pressed.text or item.colors.default.text - buffer.drawRectangle(item.x, item.y, item.width, item.height, background, foreground, " ") + screen.drawRectangle(item.x, item.y, item.width, item.height, background, foreground, " ") local y, textLimit = math.floor(item.y + item.height / 2) if not item.pressed and item.unreadCount > 0 then local tipString = tostring(item.unreadCount) local tipWidth = #tipString + 2 - buffer.drawRectangle(item.x + item.width - tipWidth - 1, y, tipWidth, 1, 0x3C3C3C, 0xE1E1E1, " ") - buffer.drawText(item.x + item.width - tipWidth, y, 0xE1E1E1, tipString) + screen.drawRectangle(item.x + item.width - tipWidth - 1, y, tipWidth, 1, 0x3C3C3C, 0xE1E1E1, " ") + screen.drawText(item.x + item.width - tipWidth, y, 0xE1E1E1, tipString) textLimit = item.width - 4 - tipWidth else textLimit = item.width - 4 end - buffer.drawText(item.x + 2, y, foreground, string.limit(item.text, textLimit, "right")) + screen.drawText(item.x + 2, y, foreground, text.limit(item.text, textLimit, "right")) end local function addContactItemToList(name) @@ -256,7 +256,7 @@ local function addContactItemToList(name) scrollBar.value = scrollBar.maximumValue rightLayout:removeChildren() - application:draw() + workspace:draw() if socketHandle then if list == channelsList then @@ -294,7 +294,7 @@ local function addChatMessage(conversationName, text, sender) local message = { text = text, - time = os.date("%H:%M", MineOSCore.time), + time = os.date("%H:%M", system.getTime()), sender = sender } @@ -318,7 +318,7 @@ local function addChatMessage(conversationName, text, sender) end chat.draw = function() - buffer.drawRectangle(chat.x, chat.y, chat.width, chat.height, 0xF0F0F0, colorScheme.otherMessageText, " ") + screen.drawRectangle(chat.x, chat.y, chat.width, chat.height, 0xF0F0F0, colorScheme.otherMessageText, " ") if history[selectedItem.text] and #history[selectedItem.text] > 0 then local y, userZoneWidth, messages = chat.y + chat.height - 2, 0, history[selectedItem.text] @@ -329,10 +329,10 @@ chat.draw = function() userZoneWidth = math.min(userZoneWidth + chatTimeWidth + 4, userZoneMaxWidth) for i = chat.y, chat.y + chat.height - 1 do - local index = buffer.getIndex(chat.x + userZoneWidth - 1, i) - local background, foreground, symbol = buffer.rawGet(index) + local index = screen.getIndex(chat.x + userZoneWidth - 1, i) + local background, foreground, symbol = screen.rawGet(index) - buffer.rawSet(index, background, 0xE1E1E1, "│") + screen.rawSet(index, background, 0xE1E1E1, "│") end for i = scrollBar.value, 1, -1 do @@ -342,10 +342,10 @@ chat.draw = function() local klitor = math.floor(pizda / 2) local vagina = pizda - klitor - buffer.drawText(chat.x + userZoneWidth + 1, y, 0xFF9280, string.rep("─", klitor) .. elda .. string.rep("─", vagina)) + screen.drawText(chat.x + userZoneWidth + 1, y, 0xFF9280, string.rep("─", klitor) .. elda .. string.rep("─", vagina)) y = y - 1 else - local wrappedLines = string.wrap(messages[i].text, chat.width - userZoneWidth - 3) + local wrappedLines = text.wrap(messages[i].text, chat.width - userZoneWidth - 3) local senderColor, textColor if messages[i].notice then @@ -361,7 +361,7 @@ chat.draw = function() end for j = #wrappedLines, 1, -1 do - buffer.drawText(chat.x + userZoneWidth + 1, y, textColor, wrappedLines[j]) + screen.drawText(chat.x + userZoneWidth + 1, y, textColor, wrappedLines[j]) y = y - 1 if y < chat.y then @@ -370,9 +370,9 @@ chat.draw = function() end local sender = messages[i].sender or selectedItem.text - local limited = string.limit(sender, userZoneWidth - chatTimeWidth - 4, "center") - buffer.drawText(chat.x + userZoneWidth - unicode.len(limited) - 2, y + 1, senderColor, limited) - buffer.drawText(chat.x + 1, y + 1, 0xC3C3C3, messages[i].time) + local limited = text.limit(sender, userZoneWidth - chatTimeWidth - 4, "center") + screen.drawText(chat.x + userZoneWidth - unicode.len(limited) - 2, y + 1, senderColor, limited) + screen.drawText(chat.x + 1, y + 1, 0xC3C3C3, messages[i].time) end y = y - 1 @@ -386,7 +386,7 @@ end loginUsernameInput.onInputFinished = function() checkLoginInputs() - application:draw() + workspace:draw() end loginPasswordInput.onInputFinished = loginUsernameInput.onInputFinished @@ -429,7 +429,7 @@ chatInput.onInputFinished = function() chatInput.text = "" - application:draw() + workspace:draw() end end @@ -444,17 +444,17 @@ local function addScrollEventHandler(layout) return height end - layout.eventHandler = function(application, layout, e1, e2, e3, e4, e5) + layout.eventHandler = function(workspace, layout, e1, e2, e3, e4, e5) if e1 == "scroll" then if e5 > 0 then if layout.cells[1][1].verticalMargin < 0 then layout.cells[1][1].verticalMargin = layout.cells[1][1].verticalMargin + 1 - application:draw() + workspace:draw() end else if layout.cells[1][1].verticalMargin > -getTotalHeight(layout) + 1 then layout.cells[1][1].verticalMargin = layout.cells[1][1].verticalMargin - 1 - application:draw() + workspace:draw() end end end @@ -470,7 +470,7 @@ local function selectItem(item) item.onTouch() end -local function userButtonOnTouch(application, object) +local function userButtonOnTouch(workspace, object) local text = object.text:gsub("^[@+]", "") if history[text] then selectItem(getItemByText(text)) @@ -524,17 +524,17 @@ local function removeUserFromList(name) end end -chat.eventHandler = function(application, chat, e1, e2, e3, e4, e5) +chat.eventHandler = function(workspace, chat, e1, e2, e3, e4, e5) if e1 == "scroll" then if e5 > 0 then if scrollBar.value > 1 then scrollBar.value = scrollBar.value - 1 - application:draw() + workspace:draw() end else if scrollBar.value < scrollBar.maximumValue then scrollBar.value = scrollBar.value + 1 - application:draw() + workspace:draw() end end elseif not e1 and socketHandle and computer.uptime() - oldUptime > socketReadDelay then @@ -584,7 +584,7 @@ chat.eventHandler = function(application, chat, e1, e2, e3, e4, e5) if ctcp == "VERSION" then sendMessage(username, "VERSION MineOS IRC Client / OpenComputers (Lua 5.3)", true, true) elseif ctcp == "TIME" then - sendMessage(username, "TIME " .. os.date(MineOSCore.time), true, true) + sendMessage(username, "TIME " .. os.date(system.getTime()), true, true) elseif command == "PING" then sendMessage(username, "PING " .. data, true, true) elseif command == "ACTION" then @@ -675,7 +675,7 @@ chat.eventHandler = function(application, chat, e1, e2, e3, e4, e5) end end - application:draw() + workspace:draw() break else break @@ -699,11 +699,11 @@ loginSubmitButton.onTouch = function() status("Connecting to server socket...") - local result, reason = component.internet.connect(config.server, config.port) + local result, reason = component.get("internet").connect(config.server, config.port) if result then socketHandle = result repeat - os.sleep(socketConnectDelay) + event.sleep(socketConnectDelay) until socketHandle.finishConnect() status("Connection estabilished, waiting for response...") @@ -728,11 +728,11 @@ settingsButton.onTouch = function() config.soundNotifications = switchAndLabel.switch.state backgroundContainer.hidden = true - application:draw() + workspace:draw() saveConfig() end - application:draw() + workspace:draw() end contactAddButton.onTouch = function() @@ -746,11 +746,11 @@ contactAddButton.onTouch = function() if #input.text > 0 and not history[input.text] then selectItem(addContactItemToList(input.text)) else - application:draw() + workspace:draw() end end - application:draw() + workspace:draw() end contactRemoveButton.onTouch = function() @@ -764,7 +764,7 @@ contactRemoveButton.onTouch = function() selectItem(systemList:getItem(socketUsername)) updateLeftLayout() - application:draw() + workspace:draw() end loginPasswordSwitchAndLabel.switch.onStateChanged = function() @@ -788,22 +788,23 @@ window.onResize = function(width, height) end scrollBar.onTouch = function() - scrollBar.value = math.round(scrollBar.value) + scrollBar.value = number.round(scrollBar.value) if scrollBar.value < 1 then scrollBar.value = 1 elseif scrollBar.value > #history[selectedItem.text] then scrollBar.value = #history[selectedItem.text] end - application:draw() + workspace:draw() end -local overrideWindowClose = window.close -window.close = function(...) +local overrideWindowRemove = window.remove +window.remove = function(...) if socketHandle then socketWrite("QUIT") end - overrideWindowClose(...) + + overrideWindowRemove(...) for key in pairs(history) do local i = 1 @@ -826,7 +827,7 @@ window.close = function(...) end end - table.toFile(historyPath, history) + filesystem.writeTable(historyPath, history) end ------------------------------------------------------------------------------- diff --git a/Applications/InfoPanel/About/English.txt b/Applications/InfoPanel/About/English.txt deleted file mode 100644 index b82f7960..00000000 --- a/Applications/InfoPanel/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Эта программа предназначена для визуального отображения информации для пользователей компьютера, она идеально впишется в ваш спавн, дом или милитаризированный бункер. Файлы с информацией хранятся в папке MineOS/System/InfoPanel, вы можете изменить их в любое время. \ No newline at end of file diff --git a/Applications/InfoPanel/About/Russian.txt b/Applications/InfoPanel/About/Russian.txt deleted file mode 100644 index b82f7960..00000000 --- a/Applications/InfoPanel/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Эта программа предназначена для визуального отображения информации для пользователей компьютера, она идеально впишется в ваш спавн, дом или милитаризированный бункер. Файлы с информацией хранятся в папке MineOS/System/InfoPanel, вы можете изменить их в любое время. \ No newline at end of file diff --git a/Applications/InfoPanel/Claims.txt b/Applications/InfoPanel/Claims.txt deleted file mode 100644 index 4ff57557..00000000 --- a/Applications/InfoPanel/Claims.txt +++ /dev/null @@ -1,15 +0,0 @@ - Приват - -Для привата и защиты от гриферства используется классический плагин -WorldGuard в совокупности с аддоном MachineGuard. - -● Приват осуществляется деревянным топором, проверка привата кожей. -● Количество блоков, доступное пользователю для привата: 700 000. -● Максимальное количество приватов на пользователя: 1. -● Приват сундуков, механизмов IC2, компьютеров и прочего создается - автоматически. -● Если вам мешает графическая подсветка выделения, используйте - команду //sel, чтобы убрать ее. -● Роботы не могут ломать блоки в привате, однако могут снимать - гаечным ключом механизмы IC2. Будьте внимательны и защищайте жилье - тщательно! \ No newline at end of file diff --git a/Applications/InfoPanel/Icon.pic b/Applications/InfoPanel/Icon.pic deleted file mode 100644 index 8f074d1222f4742b9f1a67b7d5eba556df50cffb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171 zcmXYqO$q`r42An8X{Q_S;JVik#94?iH<*QjAi5H~{g`R92`}&WcAC$n8YvwvY%~O( z{2YS5q;)V2L9Ze3?p8%Uo@gj`bZas&$(GAhwO 0 then - list:getItem(1).onTouch() - end -end - -mainContainer:drawOnScreen(true) -mainContainer:startEventHandling() \ No newline at end of file diff --git a/Applications/InfoPanel/Main.txt b/Applications/InfoPanel/Main.txt deleted file mode 100644 index afb75d12..00000000 --- a/Applications/InfoPanel/Main.txt +++ /dev/null @@ -1,21 +0,0 @@ - Главная - -Добро пожаловать на индустриальный сервер 0xFFAA00Buttex0xFFFFFF! С помощью этой панели -вы всегда можете узнать последние новости сервера и ознакомиться с -основными устоями, сложившимися в нашем обществе. Новичкам рекомендуется -прочитать все имеющиеся вкладки. - - История сервера - -Buttex существует с незапамятного 2010 года, когда кубач еще только -набирал свою популярность. Основным направлением сервера всегда были -красивые постройки, редстоун-схемы, а с недавних пор еще и кодинг -на компьютерах, предоставляемых модами. На сервере всегда царила -дружеская и домашняя атмосфера, которую мы поддерживаем из года в год. -Иногда, когда нашей теплой компании надоедало играть в кубики, мы -делали перерыв, но в скором времени сервер возрождался раз за разом. -Сейчас на дворе ноябрь, 2015 год, и мы вновь открываем Buttex для всех -желающих в новом формате - с собственным лаунчером, интересными модами -и грамотно настроенными плагинами. - - Приятной игры, дорогие мои! \ No newline at end of file diff --git a/Applications/InfoPanel/Rules.txt b/Applications/InfoPanel/Rules.txt deleted file mode 100644 index c204a560..00000000 --- a/Applications/InfoPanel/Rules.txt +++ /dev/null @@ -1,49 +0,0 @@ - Правила - -На сервере существует лишь одно правило: 0xFF5555нет никаких правил0xFFFFFF. - -Вам дозволяется абсолютно все: гриферство, читерство, мат в чате, -оскорбление админа и других игроков, ибо все это - неотъемлемая -часть нашего бытия. Вы можете быть культурным и святым человеком, -а можете крушить все направо-налево, строя хуи и свастики из грязи. -Помните, что не существует плохих или хороших деяний, есть лишь -поступки и их закономерные последствия. Таким образом, если вы -загадите личную хату админа, то он выебет вас по самые гланды, а если -будете неадекватным малым, то крайне велика вероятность, что вам -всадят нож в спину. - - Багоюз - -Любые баги - это вина администрации, безответственно отнесшейся к -исправлению ошибок плагинов и модов. Таким образом, если вы нашли -способ дюпа, способ использования недочетов сервера себе на благо, -крайне рекомендуется сообщить об этом админам - вы получите приятный -бонус за бдительность, а другие игроки получат честную игру. - - Флуд - -Если в чате творится полный бардак, и нежелательные личности флудят -или просто раздражают вас - используйте Систему Серверной Поддержки -Игроков, чтобы навести порядок: команда "0xFFAA00сервер замуть Cyka0xFFFFFF" -откроет голосование за мут игрока под ником Cyka. - - Убийства - -Вас убил сильно развитый игрок? Ну что ж, печально. Чтобы избежать -подобных эксцессов, защищайте ваше жилище тщательнее, закрывайте -дыры в обороне, копайте больше ресурсов для создания орудия мести. - - Гриферство - -Чей-то робот своровал ваши солнечные панели и механизмы IC2? Какая -неприятность! Чтобы защититься от этого, грамотно приватьте зону -вашего дома, закрывайте все щели, ставьте двери, которые нельзя -открыть редстоуном. Если вы узнали имя вора, обратитесь к обладателям -мощной брони и оружия, чтобы восстановить справедливость. - - Заключение - -Причиной всему этому "беззаконию" послужило наличие слишком больших -ограничений на большинстве серверов, где складывается впечатление, -что админы - нежные телки с завышенным ЧСВ. Но долой такую чушь! -Добро пожаловать на 0xFFAA00Buttex0xFFFFFF, где каждый сам себе хозяин! \ No newline at end of file diff --git a/Applications/InfoPanel/SSPI.txt b/Applications/InfoPanel/SSPI.txt deleted file mode 100644 index a90842aa..00000000 --- a/Applications/InfoPanel/SSPI.txt +++ /dev/null @@ -1,35 +0,0 @@ - ССПИ - -У нас имеется автоматическая 0xFFAA00Серверная Система Поддержки Игроков0xFFFFFF, -сокращенно 0xFFAA00ССПИ0xFFFFFF. Она предназначена для минимизации контактов вида -игрок-админ, заменяя по сути целый штаб модераторов. Данная система -работает через чат в виде виртуального собеседника - вводите указанные -ниже команды и получайте результат. - -● 0xFFAA00Сервер дай ресов0xFFFFFF - вы получите произвольное количество произвольных - произвольно выбранных ресурсов. -● 0xFFAA00Сервер замуть [Username]0xFFFFFF - откроется публичное голосование за мут - игрока с ником Username. -● 0xFFAA00Сервер как дела 0xFFFFFF- если ССПИ будет в хорошем расположении духа, она - может рассказать, как у нее дела. За последствия плохого настроения - системы мы ответственности не несем. -● 0xFFAA00Сервер очисти чат0xFFFFFF - очищает чат лично для вас, отправляя вам в ЛС - большое количество пробелов. -● 0xFFAA00Сервер скажи админам [сообщение]0xFFFFFF - отправляет всем администраторам - сообщение, даже если они отсутствуют на сервере. Впоследствии они - смогут его прочесть. -● 0xFFAA00Сервер вероятность [событие]0xFFFFFF - вычисляет вероятность какого-либо - события. Полезно, чтобы доказать кому-то, что он пидор. -● 0xFFAA00Сервер хеш [фраза]0xFFFFFF - возвращает хеш-сумму указанной фразы, просчитанную - по алгоритму SHA2-256. - -Следующие команды для ССПИ предназначены только для администраторов, -у простых смертных к ним нет доступа: - -● 0xFFAA00Сервер отпизди [Username]0xFFFFFF - убивает игрока с ником Username. -● 0xFFAA00Сервер сделай день/ночь0xFFFFFF - устанавливает указанное время суток. -● 0xFFAA00Сервер включи/выключи свет0xFFFFFF - управление освещение спавна. - -Кроме того, ключевое слово "Сервер" имеет несколько синонимов -для удобства: серв, сервак, ССПИ, а также все эти вариации, -написанные с заглавной буквы. \ No newline at end of file diff --git a/Applications/Installer.lua b/Applications/Installer.lua deleted file mode 100644 index e5694a60..00000000 --- a/Applications/Installer.lua +++ /dev/null @@ -1,263 +0,0 @@ -local component = require("component") -local unicode = require("unicode") -local fs = require("filesystem") -local event = require("event") -local gpu = component.gpu -local internet = component.internet - ---------------------------------------------------------------------------------------------------------------------------------- - --- Specify required files for downloading -local files = { - { - url = "https://raw.githubusercontent.com/IgorTimofeev/OpenComputers/master/lib/advancedLua.lua", - path = "/lib/advancedLua.lua" - }, - { - url = "https://raw.githubusercontent.com/IgorTimofeev/OpenComputers/master/lib/color.lua", - path = "/lib/color.lua" - }, - { - url = "https://raw.githubusercontent.com/IgorTimofeev/OpenComputers/master/lib/FormatModules/OCIF.lua", - path = "/lib/FormatModules/OCIF.lua" - }, - { - url = "https://raw.githubusercontent.com/IgorTimofeev/OpenComputers/master/lib/image.lua", - path = "/lib/image.lua" - }, - { - url = "https://raw.githubusercontent.com/IgorTimofeev/OpenComputers/master/lib/doubleBuffering.lua", - path = "/lib/doubleBuffering.lua" - }, - { - url = "https://raw.githubusercontent.com/IgorTimofeev/OpenComputers/master/lib/GUI.lua", - path = "/lib/GUI.lua" - }, -} - -local properties = { - -- Comment any coordinate to calculate it automatically (will centerize window on screen by specified axis) - -- windowX = 2, - -- windowY = 2, - -- Set window width value lower than zero (0.5 for example) to calculate it dependent on screen width - windowWidth = 54, - -- Customize offset by X axis from window corners - GUIElementsOffset = 2, - -- Customize localization as you want to - localization = { - -- Specify title of your installer - title = "GUI framework installer", - -- Use , and text insertions to automatically display their values - currentFile = "Downloading \"\"", - totalProgress = "Total progress: %", - -- Comment this lines to automatically close installer window - finished1 = "GUI framework has been successfully installed", - finished2 = "Press any key to quit" - }, - -- Customize color scheme as you want to - colors = { - window = { - background = 0xEEEEEE, - text = 0x999999, - shadow = 0x3C3C3C - }, - title = { - background = 0xCCCCCC, - text = 0x555555, - }, - progressBar = { - active = 0x0092FF, - passive = 0xCCCCCC - } - } -} - ---------------------------------------------------------------------------------------------------------------------------------- - -local screenWidth, screenHeight = gpu.getResolution() -properties.windowHeight = 8 - -if properties.windowWidth < 1 then - properties.windowWidth = math.floor(screenWidth * properties.windowWidth) -end -progressBarWidth = properties.windowWidth - properties.GUIElementsOffset * 2 - -if not properties.windowX then - properties.windowX = math.floor(screenWidth / 2 - properties.windowWidth / 2) -end - -if not properties.windowY then - properties.windowY = math.floor(screenHeight / 2 - properties.windowHeight / 2) -end - -local currentBackground, currentForeground - ---------------------------------------------------------------------------------------------------------------------------------- - -local function setBackground(color) - if currentBackground ~= color then - gpu.setBackground(color) - currentBackground = color - end -end - -local function setForeground(color) - if currentForeground ~= color then - gpu.setForeground(color) - currentForeground = color - end -end - -local function rectangle(x, y, width, height, color) - setBackground(color) - gpu.fill(x, y, width, height, " ") -end - -local function centerizedText(y, color, text) - local textLength = unicode.len(text) - if textLength > progressBarWidth then - text = unicode.sub(text, 1, progressBarWidth) - textLength = progressBarWidth - end - - setForeground(color) - gpu.set(properties.windowX + properties.GUIElementsOffset, y, string.rep(" ", progressBarWidth)) - gpu.set(math.floor(properties.windowX + properties.GUIElementsOffset + progressBarWidth / 2 - textLength / 2), y, text) -end - -local function progressBar(y, percent, text, totalProgress, currentProgress, currentFile) - setForeground(properties.colors.progressBar.passive) - gpu.set(properties.windowX + properties.GUIElementsOffset, y, string.rep("━", progressBarWidth)) - setForeground(properties.colors.progressBar.active) - gpu.set(properties.windowX + properties.GUIElementsOffset, y, string.rep("━", math.ceil(progressBarWidth * percent))) - - text = text:gsub("", totalProgress) - text = text:gsub("", currentProgress) - text = text:gsub("", currentFile) - - centerizedText(y + 1, properties.colors.window.text, text) -end - -local function download(url, path, totalProgress) - fs.makeDirectory(fs.path(path)) - - local file, fileReason = io.open(path, "w") - if file then - local pcallSuccess, requestHandle = pcall(internet.request, url) - if pcallSuccess then - if requestHandle then - -- Drawing progressbar once with zero percentage - local y = properties.windowY + 2 - progressBar(y, 0, properties.localization.currentFile, totalProgress, "0", path) - - -- Waiting for any response code - local responseCode, responseName, responseData - repeat - responseCode, responseName, responseData = requestHandle:response() - until responseCode - - -- Downloading file by chunks - local contentLength = 12 * 1024 * 1024 - local currentLength = 0 - - if responseData and responseData["Content-Length"] then - contentLength = tonumber(responseData["Content-Length"][1]) - end - - while true do - local data, reason = requestHandle.read(math.huge) - if data then - currentLength = currentLength + #data - local percent = currentLength / contentLength - progressBar(y, percent, properties.localization.currentFile, totalProgress, tostring(math.ceil(percent)), path) - - file:write(data) - else - requestHandle:close() - if reason then - error(reason) - else - file:close() - return - end - end - end - else - error("Invalid URL-address: " .. tostring(url)) - end - else - error("Usage: component.internet.request(string url)") - end - - file:close() - else - error("Failed to open file for writing: " .. tostring(fileReason)) - end -end - ---------------------------------------------------------------------------------------------------------------------------------- - --- Copying current screen data -local oldPixels = {} -for y = properties.windowY, properties.windowY + properties.windowHeight do - oldPixels[y] = {} - for x = properties.windowX, properties.windowX + properties.windowWidth do - oldPixels[y][x] = { gpu.get(x, y) } - end -end - -local function shadowPixel(x, y, symbol) - setBackground(oldPixels[y][x][3]) - gpu.set(x, y, symbol) -end - --- Vertical shadow -rectangle(properties.windowX + properties.windowWidth, properties.windowY + 1, 1, properties.windowHeight - 1, properties.colors.window.shadow) -setForeground(properties.colors.window.shadow) -shadowPixel(properties.windowX + properties.windowWidth, properties.windowY, "▄") - --- Horizontal shadow -for i = properties.windowX + 1, properties.windowX + properties.windowWidth do - shadowPixel(i, properties.windowY + properties.windowHeight, "▀") -end - --- Window background -rectangle(properties.windowX, properties.windowY + 1, properties.windowWidth, properties.windowHeight - 1, properties.colors.window.background) - --- Title -rectangle(properties.windowX, properties.windowY, properties.windowWidth, 1, properties.colors.title.background) -centerizedText(properties.windowY, properties.colors.title.text, properties.localization.title) -setBackground(properties.colors.window.background) - --- Downloading -local y = properties.windowY + 5 -progressBar(y, 0, properties.localization.totalProgress, "0", "0", files[1].path) -for i = 1, #files do - local percent = i / #files - local totalProgress = tostring(math.ceil(percent * 100)) - download(files[i].url, files[i].path, totalProgress) - progressBar(y, percent, properties.localization.totalProgress, totalProgress, "0", files[i].path) -end - --- On exit -if properties.localization.finished1 then - rectangle(properties.windowX, properties.windowY + 1, properties.windowWidth, properties.windowHeight - 1, properties.colors.window.background) - centerizedText(properties.windowY + 3, properties.colors.window.text, properties.localization.finished1) - centerizedText(properties.windowY + 4, properties.colors.window.text, properties.localization.finished2) - - while true do - local eventType = event.pull() - if eventType == "key_down" or eventType == "touch" then - break - end - end -end - --- Redraw old pixels -for y = properties.windowY, properties.windowY + properties.windowHeight do - for x = properties.windowX, properties.windowX + properties.windowWidth do - setBackground(oldPixels[y][x][3]) - setForeground(oldPixels[y][x][2]) - gpu.set(x, y, oldPixels[y][x][1]) - end -end \ No newline at end of file diff --git a/Applications/Keyboard/About/English.txt b/Applications/Keyboard/About/English.txt deleted file mode 100644 index e01f858f..00000000 --- a/Applications/Keyboard/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Программа, показывающая виртуальную клавиатуру. Полезная штука, если необходимо ограничить пользователя от взаимодействия с физической клавиатурой. \ No newline at end of file diff --git a/Applications/Keyboard/About/Russian.txt b/Applications/Keyboard/About/Russian.txt deleted file mode 100644 index e01f858f..00000000 --- a/Applications/Keyboard/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Программа, показывающая виртуальную клавиатуру. Полезная штука, если необходимо ограничить пользователя от взаимодействия с физической клавиатурой. \ No newline at end of file diff --git a/Applications/Keyboard/Icon.pic b/Applications/Keyboard/Icon.pic deleted file mode 100644 index 6ae147eca99272f4f9024b46e8101a35290cf7f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80 zcmeZw_H<+8U}5^tz^K5;0ECPjj7-cdtZeKY3=B*Pj8_>Mn3-4@nOWJHS(sRWD#7eW T(;9#>|A8_fF=ntB3z!W6PNN6u diff --git a/Applications/Keyboard/Keyboard.lua b/Applications/Keyboard/Keyboard.lua deleted file mode 100644 index 180be48c..00000000 --- a/Applications/Keyboard/Keyboard.lua +++ /dev/null @@ -1,357 +0,0 @@ - ----------------------------------------- Библиотеки ---------------------------------------- - -local component = require("component") -local event = require("event") -local unicode = require("unicode") -local term = require("term") -local gpu = component.gpu - ----------------------------------------- Переменные ---------------------------------------- - -local xSize, ySize = gpu.getResolution() - -local language = "russian" - -local width, height = 102, 22 - -local animation = false - -local keyPressDelay = 0.2 - -local currentInput = "" - -local colors = { - keyboard = 0xd0d5d9, - usualKey = 0xFFFFFF, - usualKeyText = 0x262626, - systemKey = 0x999999, - systemKeyText = 0xFFFFFF, - inputPanel = 0xFFFFFF, - inputPanelText = 0x000000, -} - -local keys = { - english = { - { {" ~ ", false, 1}, {" 1 ", false, 1}, {" 2 ", false, 1}, {" 3 ", false, 1}, {" 4 ", false, 1}, {" 5 ", false, 1}, {" 6 ", false, 1}, {" 7 ", false, 1}, {" 8 ", false, 1}, {" 9 ", false, 1}, {" 0 ", false, 1}, {" - ", false, 1}, {" + ", false, 1}, {" ⌫ ", true, 1}}, - { {" Tab ", true, 3}, {" Q ", false, 3}, {" W ", false, 3}, {" E ", false, 3}, {" R ", false, 3}, {" T ", false, 3}, {" Y ", false, 3}, {" U ", false, 3}, {" I ", false, 3}, {" O ", false, 3}, {" P ", false, 3}, {" { ", false, 3}, {" } ", false, 3}, {" | ", false, 3}}, - { {" Caps ", true, 3, false}, {" A ", false, 3}, {" S ", false, 3}, {" D ", false, 3}, {" F ", false, 3}, {" G ", false, 3}, {" H ", false, 3}, {" J ", false, 3}, {" K ", false, 3}, {" L ", false, 3}, {" : ", false, 3}, {" \" ", false, 3}, {" Enter ", true, 3}}, - { {" ⬆ ", true, 3, false}, {" Z ", false, 3}, {" X ", false, 3}, {" C ", false, 3}, {" V ", false, 3}, {" B ", false, 3}, {" N ", false, 3}, {" M ", false, 3}, {" < ", false, 3}, {" > ", false, 3}, {" ? ", false, 3}, {" ⬆ ", true, 3, false}}, - { {" .123 ", true, 3, false}, {" Alt ", true, 3, false}, {" ", false, 3}, {" Alt ", true, 3, false}, {" .123 ", true, 3, false}}, - }, - russian = { - { {" ~ ", false, 1}, {" 1 ", false, 1}, {" 2 ", false, 1}, {" 3 ", false, 1}, {" 4 ", false, 1}, {" 5 ", false, 1}, {" 6 ", false, 1}, {" 7 ", false, 1}, {" 8 ", false, 1}, {" 9 ", false, 1}, {" 0 ", false, 1}, {" - ", false, 1}, {" + ", false, 1}, {" ⌫ ", true, 1}}, - { {" Tab ", true, 3}, {" Й ", false, 3}, {" Ц ", false, 3}, {" У ", false, 3}, {" К ", false, 3}, {" Е ", false, 3}, {" Н ", false, 3}, {" Г ", false, 3}, {" Ш ", false, 3}, {" Щ ", false, 3}, {" З ", false, 3}, {" Х ", false, 3}, {" Ъ ", false, 3}, {" | ", false, 3}}, - { {" Caps ", true, 3, false}, {" Ф ", false, 3}, {" Ы ", false, 3}, {" В ", false, 3}, {" А ", false, 3}, {" П ", false, 3}, {" Р ", false, 3}, {" О ", false, 3}, {" Л ", false, 3}, {" Д ", false, 3}, {" Ж ", false, 3}, {" Э ", false, 3}, {" Enter ", true, 3}}, - { {" ⬆ ", true, 3}, {" Я ", false, 3}, {" Ч ", false, 3}, {" С ", false, 3}, {" М ", false, 3}, {" И ", false, 3}, {" Т ", false, 3}, {" Ь ", false, 3}, {" Б ", false, 3}, {" Ю ", false, 3}, {" ? ", false, 3}, {" ⬆ ", true, 3, false}}, - { {" .123 ", true, 3, false}, {" Alt ", true, 3, false}, {" ", false, 3}, {" Alt ", true, 3, false}, {" .123 ", true, 3, false}}, - }, - symbols = { - { {" ` ", false, 1}, {" 1 ", false, 1}, {" 2 ", false, 1}, {" 3 ", false, 1}, {" 4 ", false, 1}, {" 5 ", false, 1}, {" 6 ", false, 1}, {" 7 ", false, 1}, {" 8 ", false, 1}, {" 9 ", false, 1}, {" 0 ", false, 1}, {" - ", false, 1}, {" + ", false, 1}, {" ⌫ ", true, 1}}, - { {" Tab ", true, 3}, {" ~ ", false, 3}, {" ! ", false, 3}, {" @ ", false, 3}, {" # ", false, 3}, {" $ ", false, 3}, {" % ", false, 3}, {" ^ ", false, 3}, {" & ", false, 3}, {" * ", false, 3}, {" ( ", false, 3}, {" ) ", false, 3}, {" _ ", false, 3}, {" = ", false, 3}}, - { {" Caps ", true, 3, false}, {" [ ", false, 3}, {" ] ", false, 3}, {" { ", false, 3}, {" } ", false, 3}, {" € ", false, 3}, {" ₤ ", false, 3}, {" ¥ ", false, 3}, {" ; ", false, 3}, {" : ", false, 3}, {" ' ", false, 3}, {" \" ", false, 3}, {" Enter ", true, 3}}, - { {" ⬆ ", true, 3}, {" / ", false, 3}, {" \\ ", false, 3}, {" | ", false, 3}, {" < ", false, 3}, {" > ", false, 3}, {" … ", false, 3}, {" № ", false, 3}, {" , ", false, 3}, {" . ", false, 3}, {" ? ", false, 3}, {" ⬆ ", true, 3, false}}, - { {" .123 ", true, 3, false}, {" Alt ", true, 3, false}, {" ", false, 3}, {" Alt ", true, 3, false}, {" .123 ", true, 3, false}}, - }, - current = {}, - objects = {} -} -keys.current = keys[language] - ----------------------------------------- Функции ---------------------------------------- - -local function square(x, y, width, height, color) - if gpu.getBackground() ~= color then gpu.setBackground(color) end - gpu.fill(x, y, width, height, " ") -end - -local function button(x, y, height, text, background, foreground) - square(x, y, unicode.len(text), height, background) - if gpu.getForeground() ~= foreground then gpu.setForeground(foreground) end - gpu.set(x, y + math.floor(height / 2), text) -end - -local function getButtonTextData(text) - return string.gsub(text, " ", "") -end - -local function stringLimit(mode, text, size, noDots) - if unicode.len(text) <= size then return text end - local length = unicode.len(text) - if mode == "start" then - if noDots then - return unicode.sub(text, length - size + 1, -1) - else - return "…" .. unicode.sub(text, length - size + 2, -1) - end - else - if noDots then - return unicode.sub(text, 1, size) - else - return unicode.sub(text, 1, size - 1) .. "…" - end - end -end - -local function drawInfoPanel(x, y) - square(x, y, width, 3, colors.inputPanel) - if gpu.getForeground() ~= foreground then gpu.setForeground(colors.inputPanelText) end - gpu.set(x + 2, y + 1, stringLimit("start", currentInput, width - 4)) -end - -local function drawKeyboard(x, y) - - keys.objects = {} - - local xPos, yPos, widthOfKey = x, y, 0 - - drawInfoPanel(xPos, yPos) - yPos = yPos + 3 - - square(xPos, yPos, width, height - 3, colors.keyboard) - - yPos = yPos + 1 - xPos = x + 2 - - local background, foreground - for j = 1, #keys.current do - for i = 1, #keys.current[j] do - - widthOfKey = unicode.len(keys.current[j][i][1]) - - if keys.current[j][i][2] then - if keys.current[j][i][4] then - background, foreground = colors.systemKeyText, colors.systemKey - else - background, foreground = colors.systemKey, colors.systemKeyText - end - else - if keys.current[j][i][4] then - background, foreground = colors.usualKeyText, colors.usualKey - else - background, foreground = colors.usualKey, colors.usualKeyText - end - end - button(xPos, yPos, keys.current[j][i][3], keys.current[j][i][1], background, foreground) - - table.insert(keys.objects, { xPos, yPos, xPos + widthOfKey - 1, yPos + keys.current[j][i][3] - 1, keys.current[j][i][1], keys.current[j][i][2], i, j }) - - xPos = xPos + widthOfKey + 2 - end - xPos = x + 2 - yPos = yPos + keys.current[j][1][3] + 1 - end - - if animation then - gpu.setBackground(colors.inputPanel) - gpu.setForeground(colors.inputPanelText) - term.setCursorBlink(true) - xPos, yPos = x + 2, y + 1 - xPos = xPos + unicode.len(currentInput) - if xPos > x + width - 3 then xPos = x + width - 3 end - term.setCursor(xPos, yPos) - end -end - ---Ебать говнокод! Обоссы себе ебало -local isCapsPressed, isShiftPressed, isAltPressed, is123Pressed = false, false, false, false -local function pressCaps() - isCapsPressed = not isCapsPressed - keys.english[3][1][4] = not keys.english[3][1][4] - keys.russian[3][1][4] = not keys.russian[3][1][4] - keys.symbols[3][1][4] = not keys.symbols[3][1][4] -end -local function pressShift() - isShiftPressed = not isShiftPressed - keys.english[4][1][4] = not keys.english[4][1][4] - keys.english[4][12][4] = not keys.english[4][12][4] - keys.russian[4][1][4] = not keys.russian[4][1][4] - keys.russian[4][12][4] = not keys.russian[4][12][4] - keys.symbols[4][1][4] = not keys.symbols[4][1][4] - keys.symbols[4][12][4] = not keys.symbols[4][12][4] -end -local function pressAlt() - isAltPressed = not isAltPressed - keys.english[5][2][4] = not keys.english[5][2][4] - keys.english[5][4][4] = not keys.english[5][4][4] - keys.russian[5][2][4] = not keys.russian[5][2][4] - keys.russian[5][4][4] = not keys.russian[5][4][4] - keys.symbols[5][2][4] = not keys.symbols[5][2][4] - keys.symbols[5][4][4] = not keys.symbols[5][4][4] -end -local function press123() - is123Pressed = not is123Pressed - keys.english[5][1][4] = not keys.english[5][1][4] - keys.english[5][5][4] = not keys.english[5][5][4] - keys.russian[5][1][4] = not keys.russian[5][1][4] - keys.russian[5][5][4] = not keys.russian[5][5][4] - keys.symbols[5][1][4] = not keys.symbols[5][1][4] - keys.symbols[5][5][4] = not keys.symbols[5][5][4] -end - -local oldLanguage = language - ---Запомнить область пикселей и возвратить ее в виде массива -local function rememberOldPixels(x, y, x2, y2) - local newPNGMassiv = { ["backgrounds"] = {} } - local xSize, ySize = gpu.getResolution() - newPNGMassiv.x, newPNGMassiv.y = x, y - - --Перебираем весь массив стандартного PNG-вида по высоте - local xCounter, yCounter = 1, 1 - for j = y, y2 do - xCounter = 1 - for i = x, x2 do - - if (i > xSize or i < 0) or (j > ySize or j < 0) then - error("Can't remember pixel, because it's located behind the screen: x("..i.."), y("..j..") out of xSize("..xSize.."), ySize("..ySize..")\n") - end - - local symbol, fore, back = gpu.get(i, j) - - newPNGMassiv["backgrounds"][back] = newPNGMassiv["backgrounds"][back] or {} - newPNGMassiv["backgrounds"][back][fore] = newPNGMassiv["backgrounds"][back][fore] or {} - - table.insert(newPNGMassiv["backgrounds"][back][fore], {xCounter, yCounter, symbol} ) - - xCounter = xCounter + 1 - back, fore, symbol = nil, nil, nil - end - - yCounter = yCounter + 1 - end - - xSize, ySize = nil, nil - return newPNGMassiv -end - ---Нарисовать запомненные ранее пиксели из массива -local function drawOldPixels(massivSudaPihay) - --Перебираем массив с фонами - for back, backValue in pairs(massivSudaPihay["backgrounds"]) do - gpu.setBackground(back) - for fore, foreValue in pairs(massivSudaPihay["backgrounds"][back]) do - gpu.setForeground(fore) - for pixel = 1, #massivSudaPihay["backgrounds"][back][fore] do - if massivSudaPihay["backgrounds"][back][fore][pixel][3] ~= transparentSymbol then - gpu.set(massivSudaPihay.x + massivSudaPihay["backgrounds"][back][fore][pixel][1] - 1, massivSudaPihay.y + massivSudaPihay["backgrounds"][back][fore][pixel][2] - 1, massivSudaPihay["backgrounds"][back][fore][pixel][3]) - end - end - end - end -end - ----------------------------------------- Программа ---------------------------------------- - ---ecs.prepareToExit() - -local args = {...} - -local xPos, yPos = math.floor(xSize / 2 - width / 2), ySize - -local oldPixels = rememberOldPixels(xPos, ySize - height + 1, xPos + width - 1, ySize) - -if args[1] == "-a" then - yPos = ySize - height + 1 - drawKeyboard(xPos, yPos) -else - for i = 1, height do - if i == height then animation = true end - drawKeyboard(xPos, yPos) - os.sleep(0.01) - yPos = yPos - 1 - end - yPos = yPos + 1 - animation = true -end - -while true do - local e = { event.pull() } - if e[1] == "touch" then - term.setCursorBlink(false) - for i = 1, #keys.objects do - if e[3] >= keys.objects[i][1] and e[4] >= keys.objects[i][2] and e[3] <= keys.objects[i][3] and e[4] <= keys.objects[i][4] then - - local x, y, height, text, background, foreground = keys.objects[i][1], keys.objects[i][2], keys.objects[i][4] - keys.objects[i][2] + 1, keys.objects[i][5], nil, nil - - if keys.objects[i][6] then - background, foreground = colors.systemKeyText, colors.systemKey - else - background, foreground = colors.usualKeyText, colors.usualKey - end - - --Анализируем - local buttonTextData = getButtonTextData(keys.objects[i][5]) - - if buttonTextData == "Caps" then - pressCaps() - - elseif buttonTextData == "Alt" then - pressAlt() - - elseif buttonTextData == ".123" then - press123() - if language ~= "symbols" then - oldLanguage = language - language = "symbols" - else - language = oldLanguage - end - keys.current = keys[language] - - elseif buttonTextData == "⬆" then - pressShift() - - else - --Нажимаем кнопку - button(x, y, height, text, background, foreground) - --Ждем - os.sleep(keyPressDelay) - --Анализируем еще раз - - if buttonTextData == "⌫" then - currentInput = unicode.sub(currentInput, 1, -2) - - elseif buttonTextData == "Tab" then - currentInput = currentInput .. " " - elseif buttonTextData == "Enter" then - term.setCursorBlink(false) - drawOldPixels(oldPixels) - return currentInput - elseif buttonTextData == "" or buttonTextData == nil then - currentInput = currentInput .. " " - - else - if isCapsPressed or isShiftPressed then - currentInput = currentInput .. buttonTextData - else - currentInput = currentInput .. unicode.lower(buttonTextData) - end - - end - - if isShiftPressed then pressShift() end - - end - - if isAltPressed and isShiftPressed then - if language == "russian" then language = "english" elseif language == "english" then language = "russian" end - keys.current = keys[language] - drawKeyboard(xPos, yPos) - os.sleep(keyPressDelay) - pressAlt() - pressShift() - end - - --Отжимаем кнопку - drawKeyboard(xPos, yPos) - - break - end - end - end -end - - - - diff --git a/Applications/MineCodeIDE/Icon.pic b/Applications/MineCode IDE.app/Icon.pic similarity index 100% rename from Applications/MineCodeIDE/Icon.pic rename to Applications/MineCode IDE.app/Icon.pic diff --git a/Applications/MineCodeIDE/Localization/English.lang b/Applications/MineCode IDE.app/Localizations/English.lang similarity index 100% rename from Applications/MineCodeIDE/Localization/English.lang rename to Applications/MineCode IDE.app/Localizations/English.lang diff --git a/Applications/MineCodeIDE/Localization/French.lang b/Applications/MineCode IDE.app/Localizations/French.lang similarity index 100% rename from Applications/MineCodeIDE/Localization/French.lang rename to Applications/MineCode IDE.app/Localizations/French.lang diff --git a/Applications/MineCodeIDE/Localization/Russian.lang b/Applications/MineCode IDE.app/Localizations/Russian.lang similarity index 100% rename from Applications/MineCodeIDE/Localization/Russian.lang rename to Applications/MineCode IDE.app/Localizations/Russian.lang diff --git a/Applications/MineCodeIDE/Localization/Ukrainian.lang b/Applications/MineCode IDE.app/Localizations/Ukrainian.lang similarity index 100% rename from Applications/MineCodeIDE/Localization/Ukrainian.lang rename to Applications/MineCode IDE.app/Localizations/Ukrainian.lang diff --git a/Applications/MineCodeIDE/Main.lua b/Applications/MineCode IDE.app/Main.lua similarity index 92% rename from Applications/MineCodeIDE/Main.lua rename to Applications/MineCode IDE.app/Main.lua index 220bc27f..72fb658a 100755 --- a/Applications/MineCodeIDE/Main.lua +++ b/Applications/MineCode IDE.app/Main.lua @@ -1,16 +1,14 @@ -require("advancedLua") -local computer = require("computer") -local component = require("component") -local filesystem = require("filesystem") -local buffer = require("doubleBuffering") -local event = require("event") -local unicode = require("unicode") -local keyboard = require("keyboard") +local filesystem = require("Filesystem") +local screen = require("Screen") +local event = require("Event") +local keyboard = require("Keyboard") local GUI = require("GUI") -local MineOSCore = require("MineOSCore") -local MineOSPaths = require("MineOSPaths") -local MineOSInterface = require("MineOSInterface") +local internet = require("Internet") +local system = require("System") +local paths = require("Paths") +local text = require("Text") +local number = require("Number") ------------------------------------------------------------ @@ -75,9 +73,9 @@ local cursorBlinkState = false local saveContextMenuItem local cursorUptime = computer.uptime() local scriptCoroutine -local resourcesPath = MineOSCore.getCurrentScriptDirectory() -local configPath = MineOSPaths.applicationData .. "MineCode IDE/Config9.cfg" -local localization = MineOSCore.getCurrentScriptLocalization() +local currentScriptDirectory = filesystem.path(system.getCurrentScript()) +local configPath = paths.user.applicationData .. "MineCode IDE/Config9.cfg" +local localization = system.getLocalization(currentScriptDirectory .. "Localizations/") local findStartFrom local clipboard local breakpointLines @@ -89,10 +87,10 @@ local continue, showBreakpointMessage, showTip ------------------------------------------------------------ if filesystem.exists(configPath) then - config = table.fromFile(configPath) + config = filesystem.readTable(configPath) end -local application, window, menu = MineOSInterface.addWindow(GUI.window(1, 1, 120, 30)) +local workspace, window, menu = system.addWindow(GUI.window(1, 1, 120, 30)) menu:removeChildren() local codeView = window:addChild(GUI.codeView(1, 1, 1, 1, 1, 1, 1, {}, {}, GUI.LUA_SYNTAX_PATTERNS, config.syntaxColorScheme, config.syntaxHighlight, lines)) @@ -115,16 +113,16 @@ codeView.draw = function(...) x <= codeView.codeAreaPosition + codeView.codeAreaWidth - 2 and y <= codeView.y + codeView.height - 2 then - buffer.drawText(x, y, config.cursorColor, config.cursorSymbol) + screen.drawText(x, y, config.cursorColor, config.cursorSymbol) end end -- if autocompleteDatabase then -- local w = 30 -- local x, y = codeView.x + codeView.width - w, codeView.y - -- buffer.drawRectangle(x, y, w, codeView.height, 0x0, 0xFFFFFF, " ") + -- screen.drawRectangle(x, y, w, codeView.height, 0x0, 0xFFFFFF, " ") -- for key, value in pairs(autocompleteDatabase) do - -- buffer.drawText(x + 1, y, 0xFFFFFF, key .. ": " .. value) + -- screen.drawText(x + 1, y, 0xFFFFFF, key .. ": " .. value) -- y = y + 1 -- if y > codeView.y + codeView.height - 1 then -- break @@ -134,7 +132,7 @@ codeView.draw = function(...) end local function saveConfig() - table.toFile(configPath, config) + filesystem.writeTable(configPath, config) end local topToolBar = window:addChild(GUI.container(1, 1, 1, 3)) @@ -161,16 +159,16 @@ local titleDebugMode = false title.eventHandler = nil title.draw = function() local sides = titleDebugMode and 0xCC4940 or 0x5A5A5A - buffer.drawRectangle(title.x, title.y, 1, title.height, sides, 0x0, " ") - buffer.drawRectangle(title.x + title.width - 1, title.y, 1, title.height, sides, 0x0, " ") - buffer.drawRectangle(title.x + 1, title.y, title.width - 2, 3, titleDebugMode and 0x880000 or 0x3C3C3C, 0x969696, " ") + screen.drawRectangle(title.x, title.y, 1, title.height, sides, 0x0, " ") + screen.drawRectangle(title.x + title.width - 1, title.y, 1, title.height, sides, 0x0, " ") + screen.drawRectangle(title.x + 1, title.y, title.width - 2, 3, titleDebugMode and 0x880000 or 0x3C3C3C, 0x969696, " ") if titleDebugMode then local text = lastErrorLine and localization.runtimeError or localization.debugging .. (_G.MineCodeIDEDebugInfo and _G.MineCodeIDEDebugInfo.line or "N/A") - buffer.drawText(math.floor(title.x + title.width / 2 - unicode.len(text) / 2), title.y + 1, 0xE1E1E1, text) + screen.drawText(math.floor(title.x + title.width / 2 - unicode.len(text) / 2), title.y + 1, 0xE1E1E1, text) else for i = 1, #titleLines do - buffer.drawText(math.floor(title.x + title.width / 2 - unicode.len(titleLines[i]) / 2), title.y + i - 1, 0x969696, titleLines[i]) + screen.drawText(math.floor(title.x + title.width / 2 - unicode.len(titleLines[i]) / 2), title.y + i - 1, 0x969696, titleLines[i]) end end end @@ -186,7 +184,7 @@ toggleTopToolBarButton.switchMode, toggleTopToolBarButton.pressed = true, true local actionButtons = window:addChild(GUI.actionButtons(2, 2, true)) actionButtons.close.onTouch = function() - window:close() + window:remove() end actionButtons.maximize.onTouch = function() window:maximize() @@ -225,8 +223,8 @@ end local function updateTitle() if not topToolBar.hidden then - titleLines[1] = string.limit(leftTreeView.selectedItem or "...", title.width - 4, "left") - titleLines[2] = string.limit(localization.cursor .. math.floor(cursorPositionLine) .. localization.line .. math.floor(cursorPositionSymbol) .. localization.symbol, title.width - 4) + titleLines[1] = text.limit(leftTreeView.selectedItem or "...", title.width - 4, "left") + titleLines[2] = text.limit(localization.cursor .. math.floor(cursorPositionLine) .. localization.line .. math.floor(cursorPositionSymbol) .. localization.symbol, title.width - 4) if codeView.selections[1] then local countOfSelectedLines, countOfSelectedSymbols = codeView.selections[1].to.line - codeView.selections[1].from.line + 1 @@ -243,9 +241,9 @@ local function updateTitle() countOfSelectedSymbols = countOfSelectedSymbols + unicode.len(unicode.sub(lines[codeView.selections[1].to.line], 1, codeView.selections[1].to.symbol)) end - titleLines[3] = string.limit(localization.selection .. math.floor(countOfSelectedLines) .. localization.lines .. math.floor(countOfSelectedSymbols) .. localization.symbols, title.width - 4) + titleLines[3] = text.limit(localization.selection .. math.floor(countOfSelectedLines) .. localization.lines .. math.floor(countOfSelectedSymbols) .. localization.symbols, title.width - 4) else - titleLines[3] = string.limit(localization.selection .. localization.none, title.width - 4) + titleLines[3] = text.limit(localization.selection .. localization.none, title.width - 4) end end end @@ -253,7 +251,7 @@ end local function tick(state) cursorBlinkState = state updateTitle() - application:draw() + workspace:draw() cursorUptime = computer.uptime() end @@ -524,7 +522,7 @@ local function optimizeString(s) end local function addBackgroundContainer(title) - return GUI.addBackgroundContainer(application, true, true, title) + return GUI.addBackgroundContainer(workspace, true, true, title) end local function addInputFadeContainer(title, placeholder) @@ -546,7 +544,7 @@ local function newFile() end local function openFile(path) - local file, reason = io.open(path, "r") + local file, reason = filesystem.open(path, "r") if file then newFile() leftTreeView.selectedItem = path @@ -570,7 +568,7 @@ local function openFile(path) if counter % config.linesToShowOpenProgress == 0 then progressBar.value = math.floor(currentSize / totalSize * 100) computer.pullSignal(0) - application:draw() + workspace:draw() end end @@ -582,7 +580,7 @@ local function openFile(path) if counter > config.linesToShowOpenProgress then progressBar.value = 100 - application:draw() + workspace:draw() end codeView.hidden = false @@ -597,7 +595,7 @@ end local function saveFile(path) filesystem.makeDirectory(filesystem.path(path)) - local file, reason = io.open(path, "w") + local file, reason = filesystem.open(path, "w") if file then for line = 1, #lines do file:write(lines[line], "\n") @@ -617,25 +615,25 @@ local function gotoLineWindow() if container.input.text:match("%d+") then gotoLine(tonumber(container.input.text)) container:remove() - application:draw() + workspace:draw() end end - application:draw() + workspace:draw() end local function openFileWindow() - local filesystemDialog = GUI.addFilesystemDialog(application, true, 50, math.floor(window.height * 0.8), "Open", "Cancel", "File name", "/") + local filesystemDialog = GUI.addFilesystemDialog(workspace, true, 50, math.floor(window.height * 0.8), "Open", "Cancel", "File name", "/") filesystemDialog:setMode(GUI.IO_MODE_OPEN, GUI.IO_MODE_FILE) filesystemDialog.onSubmit = function(path) openFile(path) - application:draw() + workspace:draw() end filesystemDialog:show() end local function saveFileAsWindow() - local filesystemDialog = GUI.addFilesystemDialog(application, true, 50, math.floor(window.height * 0.8), "Save", "Cancel", "File name", "/") + local filesystemDialog = GUI.addFilesystemDialog(workspace, true, 50, math.floor(window.height * 0.8), "Save", "Cancel", "File name", "/") filesystemDialog:setMode(GUI.IO_MODE_SAVE, GUI.IO_MODE_FILE) filesystemDialog.onSubmit = function(path) saveFile(path) @@ -643,7 +641,7 @@ local function saveFileAsWindow() leftTreeView.selectedItem = (leftTreeView.workPath .. path):gsub("/+", "/") updateTitle() - application:draw() + workspace:draw() end filesystemDialog:show() end @@ -678,9 +676,9 @@ local function downloadFileFromWeb() if #container.input.text > 0 then container.input:remove() container.layout:addChild(GUI.text(1, 1, 0x969696, localization.downloading)) - application:draw() + workspace:draw() - local result, reason = require("web").request(container.input.text) + local result, reason = internet.request(container.input.text) if result then newFile() lines, codeView.maximumLineLength = splitStringIntoLines(result) @@ -692,10 +690,10 @@ local function downloadFileFromWeb() end container:remove() - application:draw() + workspace:draw() end - application:draw() + workspace:draw() end local function getVariables(codePart) @@ -728,9 +726,8 @@ end continue = function(...) -- Готовим экран к запуску - local oldResolutionX, oldResolutionY = component.gpu.getResolution() - MineOSInterface.clearTerminal() - + local oldResolutionX, oldResolutionY = screen.getGPUProxy().getResolution() + -- Запускаем _G.MineCodeIDEDebugInfo = nil local coroutineResumeSuccess, coroutineResumeReason = coroutine.resume(scriptCoroutine, ...) @@ -738,20 +735,19 @@ continue = function(...) -- Анализируем результат запуска if coroutineResumeSuccess then if coroutine.status(scriptCoroutine) == "dead" then - MineOSInterface.waitForPressingAnyKey() - buffer.setResolution(oldResolutionX, oldResolutionY) - application:draw(true) + screen.setResolution(oldResolutionX, oldResolutionY) + workspace:draw(true) else -- Тест на пидора, мало ли у чувака в проге тоже есть yield if _G.MineCodeIDEDebugInfo then - buffer.setResolution(oldResolutionX, oldResolutionY) - application:draw(true) + screen.setResolution(oldResolutionX, oldResolutionY) + workspace:draw(true) gotoLine(_G.MineCodeIDEDebugInfo.line) showBreakpointMessage(_G.MineCodeIDEDebugInfo.variables) end end else - buffer.setResolution(oldResolutionX, oldResolutionY) + screen.setResolution(oldResolutionX, oldResolutionY) showTip(debug.traceback(scriptCoroutine, coroutineResumeReason), "%:(%d+)%: in main chunk", true, true) end end @@ -802,10 +798,10 @@ local function zalupa() updateHighlights() container:remove() - application:draw() + workspace:draw() end - container:addChild(GUI.object(1, 1, window.width, window.height)).eventHandler = function(application, object, e1) + container:addChild(GUI.object(1, 1, window.width, window.height)).eventHandler = function(workspace, object, e1) if e1 == "touch" or e1 == "key_down" then container.close() end @@ -842,23 +838,23 @@ showTip = function(errorCode, matchCode, beep, force) tip.passScreenEvents = true tip.draw = function() - buffer.drawText(math.floor(tip.x + tip.width / 2 - 1), tip.y, 0xE1E1E1, "◢◣") - buffer.drawRectangle(tip.x, tip.y + 1, tip.width, tip.height - 1, 0xE1E1E1, 0x2D2D2D, " ") + screen.drawText(math.floor(tip.x + tip.width / 2 - 1), tip.y, 0xE1E1E1, "◢◣") + screen.drawRectangle(tip.x, tip.y + 1, tip.width, tip.height - 1, 0xE1E1E1, 0x2D2D2D, " ") for i = 1, #tipLines do - buffer.drawText(tip.x + 1, tip.y + i + 1, 0x2D2D2D, tipLines[i]) + screen.drawText(tip.x + 1, tip.y + i + 1, 0x2D2D2D, tipLines[i]) end end - tipLines = string.wrap(errorCode, tip.width - 2) + tipLines = text.wrap(errorCode, tip.width - 2) tip.height = #tipLines + 3 local minX = codeView.localX + codeView.codeAreaPosition - codeView.x local maxX = minX + codeView.width - tip.width - 5 - tip.localX = math.min(maxX, math.max(minX + 1, math.round(minX + unicode.len(lines[lastErrorLine]) / 2 - tip.width / 2))) + tip.localX = math.min(maxX, math.max(minX + 1, number.round(minX + unicode.len(lines[lastErrorLine]) / 2 - tip.width / 2))) tip.localY = codeView.localY + lastErrorLine - codeView.fromLine + 1 - application:draw(force) + workspace:draw(force) if beep then computer.beep(1500, 0.08) @@ -900,7 +896,7 @@ showBreakpointMessage = function(variables) end titleDebugMode = true - application:draw() + workspace:draw() computer.beep(1500, 0.08) end @@ -916,12 +912,12 @@ local function launchWithArgumentsWindow() end container:remove() - application:draw() + workspace:draw() run(table.unpack(arguments)) end - application:draw() + workspace:draw() end local function deleteLine(line) @@ -1039,7 +1035,7 @@ local function selectAndPasteColor() palette.cancelButton.onTouch = function() palette:remove() - application:draw() + workspace:draw() end palette.submitButton.onTouch = function() @@ -1292,7 +1288,7 @@ local function toggleBottomToolBar() calculateSizes() if not bottomToolBar.hidden then - application:draw() + workspace:draw() findFromFirstDisplayedLine() end end @@ -1362,7 +1358,7 @@ local function createEditOrRightClickMenu(menu) menu:addItem(localization.addBreakpoint, false, "F9").onTouch = function() addBreakpoint() - application:draw() + workspace:draw() end menu:addItem(localization.clearBreakpoints, not breakpointLines, "^F9").onTouch = function() @@ -1371,10 +1367,10 @@ local function createEditOrRightClickMenu(menu) end local uptime = computer.uptime() -codeView.eventHandler = function(application, object, e1, e2, e3, e4, e5) +codeView.eventHandler = function(workspace, object, e1, e2, e3, e4, e5) if e1 == "touch" then if e5 == 1 then - createEditOrRightClickMenu(GUI.addContextMenu(application, e3, e4)) + createEditOrRightClickMenu(GUI.addContextMenu(workspace, e3, e4)) else setCursorPositionAndClearSelection(convertScreenCoordinatesToTextPosition(e3, e4)) end @@ -1586,9 +1582,9 @@ codeView.eventHandler = function(application, object, e1, e2, e3, e4, e5) end leftTreeView.onItemSelected = function(path) - application:draw() + workspace:draw() openFile(path) - application:draw() + workspace:draw() end local MineCodeContextMenu = menu:addContextMenu("MineCode", 0x0) @@ -1618,13 +1614,13 @@ MineCodeContextMenu:addItem(localization.about).onTouch = function() textBox:setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) textBox.eventHandler = nil - application:draw() + workspace:draw() end local fileContextMenu = menu:addContextMenu(localization.file) fileContextMenu:addItem(localization.new, false, "^N").onTouch = function() newFile() - application:draw() + workspace:draw() end fileContextMenu:addItem(localization.open, false, "^O").onTouch = function() @@ -1647,15 +1643,15 @@ fileContextMenu:addItem(localization.saveAs, false, "^⇧S").onTouch = function( saveFileAsWindow() end -fileContextMenu:addItem(MineOSCore.localization.flashEEPROM, not component.isAvailable("eeprom")).onTouch = function() - local container = addBackgroundContainer(MineOSCore.localization.flashEEPROM) - container.layout:addChild(GUI.label(1, 1, container.width, 1, 0x969696, MineOSCore.localization.flashingEEPROM .. "...")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) - application:draw() +fileContextMenu:addItem(system.localization.flashEEPROM, not component.isAvailable("eeprom")).onTouch = function() + local container = addBackgroundContainer(system.localization.flashEEPROM) + container.layout:addChild(GUI.label(1, 1, container.width, 1, 0x969696, system.localization.flashingEEPROM .. "...")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + workspace:draw() - pcall(component.eeprom.set, table.concat(lines, ";")) + pcall(component.get("eeprom").set, table.concat(lines, ";")) container:remove() - application:draw() + workspace:draw() end fileContextMenu:addSeparator() @@ -1692,7 +1688,7 @@ end local propertiesContextMenu = menu:addContextMenu(localization.properties) propertiesContextMenu:addItem(localization.colorScheme).onTouch = function() - local container = GUI.addBackgroundContainer(application, true, false, localization.colorScheme) + local container = GUI.addBackgroundContainer(workspace, true, false, localization.colorScheme) local colorSelectorsCount, colorSelectorCountX = 0, 4; for key in pairs(config.syntaxColorScheme) do colorSelectorsCount = colorSelectorsCount + 1 end local colorSelectorCountY = math.ceil(colorSelectorsCount / colorSelectorCountX) @@ -1722,7 +1718,7 @@ propertiesContextMenu:addItem(localization.colorScheme).onTouch = function() end end - application:draw() + workspace:draw() end propertiesContextMenu:addItem(localization.cursorProperties).onTouch = function() @@ -1748,7 +1744,7 @@ propertiesContextMenu:addItem(localization.cursorProperties).onTouch = function( saveConfig() end - application:draw() + workspace:draw() end if topToolBar.hidden then @@ -1785,13 +1781,13 @@ end addBreakpointButton.onTouch = function() addBreakpoint() - application:draw() + workspace:draw() end syntaxHighlightingButton.onTouch = function() config.syntaxHighlight = not config.syntaxHighlight codeView.syntaxHighlight = config.syntaxHighlight - application:draw() + workspace:draw() saveConfig() end @@ -1799,19 +1795,19 @@ toggleLeftToolBarButton.onTouch = function() leftTreeView.hidden = not toggleLeftToolBarButton.pressed leftTreeViewResizer.hidden = leftTreeView.hidden calculateSizes() - application:draw() + workspace:draw() end toggleBottomToolBarButton.onTouch = function() bottomToolBar.hidden = not toggleBottomToolBarButton.pressed calculateSizes() - application:draw() + workspace:draw() end toggleTopToolBarButton.onTouch = function() topToolBar.hidden = not toggleTopToolBarButton.pressed calculateSizes() - application:draw() + workspace:draw() end codeView.verticalScrollBar.onTouch = function() @@ -1826,7 +1822,7 @@ runButton.onTouch = function() run() end -autocomplete.onItemSelected = function(application, object, e1) +autocomplete.onItemSelected = function(workspace, object, e1) local firstPart = unicode.sub(lines[cursorPositionLine], 1, autoCompleteWordStart - 1) local secondPart = unicode.sub(lines[cursorPositionLine], autoCompleteWordEnd + 1, -1) local middle = firstPart .. autocomplete.items[autocomplete.selectedItem] @@ -1843,7 +1839,7 @@ end window.onResize = function(width, height) calculateSizes() - application:draw() + workspace:draw() end searchInput.onInputFinished = findFromFirstDisplayedLine @@ -1855,7 +1851,7 @@ searchButton.onTouch = find autocomplete:moveToFront() leftTreeView:updateFileList() calculateSizes() -application:draw() +workspace:draw() local initialPath = select(1, ...) if initialPath and filesystem.exists(initialPath) then @@ -1864,4 +1860,4 @@ else newFile() end -application:draw() +workspace:draw() diff --git a/Applications/MineSweeper/About/English.txt b/Applications/MineSweeper/About/English.txt deleted file mode 100644 index 8e4a7539..00000000 --- a/Applications/MineSweeper/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Известная игра "Сапер", написанная товарищем QwertyMan с форума ComputerCraft.ru. \ No newline at end of file diff --git a/Applications/MineSweeper/About/Russian.txt b/Applications/MineSweeper/About/Russian.txt deleted file mode 100644 index 8e4a7539..00000000 --- a/Applications/MineSweeper/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Известная игра "Сапер", написанная товарищем QwertyMan с форума ComputerCraft.ru. \ No newline at end of file diff --git a/Applications/MineSweeper/Icon.pic b/Applications/MineSweeper/Icon.pic deleted file mode 100644 index d1f56db3f6d2c042c604005d9b55b0d01844da57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 125 zcmXYnu?>JQ3;H mSTakJTvDtIcRSJ1kVe(&_J$2{y8?NJX9jh<1M&aZMEw9jQWlf| diff --git a/Applications/MineSweeper/MineSweeper.lua b/Applications/MineSweeper/MineSweeper.lua deleted file mode 100644 index 9d29610a..00000000 --- a/Applications/MineSweeper/MineSweeper.lua +++ /dev/null @@ -1,216 +0,0 @@ --- Автор: qwertyMAN --- Версия: 0.1 beta - -local term = require("term") -local event = require("event") -local component = require("component") -local gpu = component.gpu -local max_mine = 40 -- число мин на поле -local display = {gpu.getResolution()} -local border = {0,0} -- отступ, который не используется -local marker = {} -- отмечает ПКМ мины -local size = {16,16} -- размер игрового поля -math.randomseed(os.time()) - -local colors={0x0000ff,0x00ff00,0xff0000,0xffff00,0x8800ff,0x88ff00,0x00ffff,0xff00ff} - -local function conv_cord(sx,sy) - return sx*2-1+border[1], sy+border[2] -end - --- создаем поле -local area={} -for x=1, size[1] do - area[x]={} - for y=1, size[2] do - area[x][y]={mine=false, n=0} - end -end - --- генерируем мины -for i=1, max_mine do - while true do - rand_x, rand_y = math.random(1, size[1]), math.random(1, size[2]) - if not area[rand_x][rand_y].mine then - area[rand_x][rand_y].mine = true - break - end - end -end - --- генерирем числа на пустых клетках -for x=1, size[1] do - for y=1, size[2] do - if not area[x][y].mine then - for i=-1,1 do - for j=-1,1 do - if x+i>0 and y+j>0 and x+i0 then -- если не пустая - gpu.setForeground(colors[area[nx][ny].n]) - local rezerv = {conv_cord(nx,ny)} - gpu.set(rezerv[1], rezerv[2], " "..area[nx][ny].n) - else - -- если пустая - for i=-1,1 do - for j=-1,1 do - local mx,my = nx+i, ny+j - -- если ячейка существует - if mx>=1 and my>=1 and mx<=size[1] and my<=size[2] then - local swich = true - -- проверяем есть ли она в бд - for n=1, #sorting_tb do - if mx==sorting_tb[n][1] and my==sorting_tb[n][2] then - swich = false - end - end - for n=1, #not_sorting_tb do - if mx==not_sorting_tb[n][1] and my==not_sorting_tb[n][2] then - swich = false - end - end - if swich then - local rezerv = {conv_cord(mx,my)} - gpu.set(rezerv[1], rezerv[2], " ") - not_sorting_tb[#not_sorting_tb+1]={mx,my} - end - end - end - end - end - area[nx][ny]=false - end - sorting_tb[#sorting_tb+1] = not_sorting_tb[1] - table.remove(not_sorting_tb,1) - end - end -end - --- тело программы -while true do - local _,_,x,y,key,nick = event.pull("touch") - local x,y = click(x,y) - if key == 0 then - local swich = true - for i=1, #marker do - if x == marker[i][1] and y == marker[i][2] then - swich = false - end - end - if area[x][y] and area[x][y].mine and swich then - -- покажим мины - gpu.setBackground(0xff0000) - gpu.setForeground(0x000000) - for i=1, size[1] do - for j=1, size[2] do - if area[i][j] and area[i][j].mine then - local rezerv = {conv_cord(i,j)} - gpu.set(rezerv[1], rezerv[2], " m") - end - end - end - gpu.setBackground(0x000000) - gpu.setForeground(0xffffff) - os.sleep(2) - term.clear() - print("game over") - os.sleep(2) - term.clear() - return - elseif swich then - open(x,y) - -- проверяем выигрыш - local timer = 0 - for i=1, size[1] do - for j=1, size[2] do - if area[i][j] then - timer = timer+1 - end - end - end - if timer==max_mine then - -- покажим мины - gpu.setBackground(0xff0000) - gpu.setForeground(0x000000) - for i=1, size[1] do - for j=1, size[2] do - if area[i][j] and area[i][j].mine then - local rezerv = {conv_cord(i,j)} - gpu.set(rezerv[1], rezerv[2], " m") - end - end - end - gpu.setBackground(0x000000) - gpu.setForeground(0xffffff) - -- поздравления - os.sleep(2) - term.clear() - print("You win!") - os.sleep(2) - term.clear() - return - end - end - elseif key == 1 then - local swich = true - for i=#marker, 1, -1 do - if x == marker[i][1] and y == marker[i][2] then - table.remove(marker,i) - gpu.setBackground(0xAAAAAA) - local rezerv = {conv_cord(x,y)} - gpu.set(rezerv[1], rezerv[2], " ") - swich = false - end - end - if swich and area[x][y] then - marker[#marker+1]={x,y} - gpu.setBackground(0xffaa00) - local rezerv = {conv_cord(x,y)} - gpu.set(rezerv[1], rezerv[2], " ") - end - gpu.setBackground(0x000000) - end -end \ No newline at end of file diff --git a/Applications/MultiScreen/MultiScreen.lua b/Applications/MultiScreen/MultiScreen.lua deleted file mode 100644 index dd740cdc..00000000 --- a/Applications/MultiScreen/MultiScreen.lua +++ /dev/null @@ -1,297 +0,0 @@ - -local component = require("component") -local color = require("color") -local buffer = require("doubleBuffering") -local GUI = require("GUI") -local bit32 = require("bit32") -local event = require("event") -local unicode = require("unicode") -local fs = require("filesystem") - --------------------------------------------------------------------------------- - -local configPath = "/MultiScreen.cfg" -local elementWidth = 48 -local baseResolutionWidth = 146 -local baseResolutionHeight = 54 -local GPUProxy = buffer.getGPUProxy() -local mainScreenAddress = GPUProxy.getScreen() - -local config = { - backgroundColor = 0x0, -} - --------------------------------------------------------------------------------- - -if fs.exists(configPath) then - config = table.fromFile(configPath) -end - -local function saveConfig() - table.toFile(configPath, config, true) -end - -local application = GUI.application() -application:addChild(GUI.panel(1, 1, application.width, application.height, 0x2D2D2D)) - -local layout = application:addChild(GUI.layout(1, 1, application.width, application.height, 1, 1)) - -local function clearScreens() - for address in component.list("screen") do - if address ~= mainScreenAddress then - GPUProxy.bind(address, false) - GPUProxy.setDepth(8) - GPUProxy.setResolution(baseResolutionWidth, baseResolutionHeight) - GPUProxy.setBackground(config.backgroundColor) - GPUProxy.fill(1, 1, baseResolutionWidth, baseResolutionHeight, " ") - end - end - - GPUProxy.bind(mainScreenAddress, false) -end - -local function addButton(text) - return layout:addChild(GUI.button(1, 1, elementWidth, 3, 0x4B4B4B, 0xD2D2D2, 0xD2D2D2, 0x4B4B4B, text)) -end - -local function addTextBox(lines) - local textBox = layout:addChild(GUI.textBox(1, 1, elementWidth, 16, nil, 0x969696, lines, 1, 0, 0, true, true)) - textBox.eventHandler = nil - - return textBox -end - -local function mainMenu(force) - layout:removeChildren() - - local lines = { - {color = 0xE1E1E1, text = "Welcome to MultiScreen software"}, - " ", - "Here you can combine multiple screens into a single cluster and draw huge images saved in OCIF5 format. There's some useful tips for best experience:", - "• Use maximum size constructions for each screen (8x6 blocks)", - "• Connect more power sources to screens if they're blinking", - " " - } - - if config.map then - table.insert(lines, {color = 0xE1E1E1, text = "Current cluster properties:"}) - table.insert(lines, " ") - local width, height = #config.map[1], #config.map - table.insert(lines, width .. "x" .. height .. " screen blocks") - table.insert(lines, width * baseResolutionWidth .. "x" .. height * baseResolutionHeight .. " OpenComputers pixels") - table.insert(lines, width * baseResolutionWidth * 2 .. "x" .. height * baseResolutionHeight * 4 .. " pixels using Braille font") - else - table.insert(lines, {color = 0xE1E1E1, text = "Calibrate your system before starting"}) - end - - addTextBox(lines) - - local actionComboBox = layout:addChild(GUI.comboBox(1, 1, elementWidth, 3, 0xEEEEEE, 0x2D2D2D, 0x3C3C3C, 0x888888)) - actionComboBox:addItem("Draw image") - actionComboBox:addItem("Clear screens") - actionComboBox:addItem("Calibrate") - - local filesystemChooser = layout:addChild(GUI.filesystemChooser(1, 1, elementWidth, 3, 0xE1E1E1, 0x888888, 0x3C3C3C, 0x888888, nil, "Open", "Cancel", "Choose file", "/")) - filesystemChooser:setMode(GUI.IO_MODE_OPEN, GUI.IO_MODE_FILE) - filesystemChooser:addExtensionFilter(".pic") - - local colorSelector = layout:addChild(GUI.colorSelector(2, 2, elementWidth, 3, config.backgroundColor, "Choose color")) - - local actionButton = addButton("Next") - - actionComboBox.onItemSelected = function() - filesystemChooser.hidden = actionComboBox.selectedItem ~= 1 - colorSelector.hidden = actionComboBox.selectedItem ~= 2 - actionButton.disabled = actionComboBox.selectedItem == 1 and (not filesystemChooser.path or not config.map) - end - - filesystemChooser.onSubmit = function() - actionComboBox.onItemSelected() - application:draw() - end - - actionButton.onTouch = function() - if actionComboBox.selectedItem == 1 then - local file = io.open(filesystemChooser.path, "rb") - - local signature = file:read(4) - if signature == "OCIF" then - local encodingMethod = string.byte(file:read(1)) - if encodingMethod == 5 then - local width = bit32.bor(bit32.lshift(string.byte(file:read(1)), 8), string.byte(file:read(1))) - local height = bit32.bor(bit32.lshift(string.byte(file:read(1)), 8), string.byte(file:read(1))) - - clearScreens() - - local background, foreground, symbol, currentBackground, currentForeground, currentAddress - for y = 1, height do - for x = 1, width do - background = color.to24Bit(string.byte(file:read(1))) - foreground = color.to24Bit(string.byte(file:read(1))) - file:read(1) - symbol = fs.readUnicodeChar(file) - - local xMonitor = math.ceil(x / baseResolutionWidth) - local yMonitor = math.ceil(y / baseResolutionHeight) - - if config.map[yMonitor] and config.map[yMonitor][xMonitor] then - if currentAddress ~= config.map[yMonitor][xMonitor] then - GPUProxy.bind(config.map[yMonitor][xMonitor], false) - GPUProxy.setBackground(background) - GPUProxy.setForeground(foreground) - - currentAddress, currentBackground, currentForeground = config.map[yMonitor][xMonitor], background, foreground - end - - if currentBackground ~= background then - GPUProxy.setBackground(background) - currentBackground = background - end - - if currentForeground ~= foreground then - GPUProxy.setForeground(foreground) - currentForeground = foreground - end - - GPUProxy.set(x - (xMonitor - 1) * baseResolutionWidth, y - (yMonitor - 1) * baseResolutionHeight, symbol) - end - end - end - - file:close() - - GPUProxy.bind(mainScreenAddress, false) - GUI.alert("Done.") - else - file:close() - GUI.alert("Wrong encodingMethod: " .. tostring(encodingMethod)) - end - else - file:close() - GUI.alert("Wrong signature: " .. tostring(signature)) - end - elseif actionComboBox.selectedItem == 2 then - config.backgroundColor = colorSelector.color - saveConfig() - clearScreens() - else - layout:removeChildren() - - addTextBox({ - {color = 0xE1E1E1, text = "Screen cluster calibration"}, - " ", - "Specify required count of screens (not screen blocks, screens!) by horizontal and vertical" - }) - - local hSlider = layout:addChild(GUI.slider(1, 1, elementWidth, 0x66DB80, 0x0, 0xFFFFFF, 0xAAAAAA, 1, 10, 5, false, "Screens by horizontal: ", "")) - hSlider.roundValues = true - hSlider.height = 2 - - local vSlider = layout:addChild(GUI.slider(1, 1, elementWidth, 0x66DB80, 0x0, 0xFFFFFF, 0xAAAAAA, 1, 10, 4, false, "Screens by vertical: ", "")) - vSlider.roundValues = true - vSlider.height = 2 - - addButton("Next").onTouch = function() - local connectedCount = -1 - for address in component.list("screen") do - connectedCount = connectedCount + 1 - end - - hSlider.value, vSlider.value = math.floor(hSlider.value), math.floor(vSlider.value) - local specifiedCount = hSlider.value * vSlider.value - - if specifiedCount <= connectedCount then - layout:removeChildren() - - addTextBox({ - {color = 0xE1E1E1, text = "Screen cluster calibration"}, - " ", - "Touch highlighted screen with your hand once. After touching all of screens calibration will be finished" - }) - - local SSX, SSY = 1, 1 - local function screenObjectDraw(object) - buffer.drawRectangle(object.x, object.y, object.width, object.height, (SSX == object.SX and SSY == object.SY) and 0x22FF22 or 0xE1E1E1, 0x0, " ") - end - - local function newScreen(SX, SY) - local object = GUI.object(1, 1, 8, 3) - object.draw = screenObjectDraw - object.SX = SX - object.SY = SY - - return object - end - - local function newScreenLine(SY) - local lineLayout = GUI.layout(1, 1, layout.width, 3, 1, 1) - lineLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL) - lineLayout:setSpacing(1, 1, 2) - - for SX = 1, hSlider.value do - lineLayout:addChild(newScreen(SX, SY)) - end - - return lineLayout - end - - for SY = 1, vSlider.value do - layout:addChild(newScreenLine(SY)) - end - - application:draw() - - clearScreens(0x0) - - config.map = {} - local hue, hueStep = 0, 360 / specifiedCount - while true do - local e1, e2 = event.pull("touch") - if e2 ~= mainScreenAddress then - GPUProxy.bind(e2, false) - GPUProxy.setDepth(8) - GPUProxy.setResolution(baseResolutionWidth, baseResolutionHeight) - GPUProxy.setBackground(color.HSBToInteger(hue, 1, 1)) - GPUProxy.setForeground(0x0) - GPUProxy.fill(1, 1, baseResolutionWidth, baseResolutionHeight, " ") - - local text = "Screen " .. SSX .. "x" .. SSY .. " has been calibrated" - GPUProxy.set(math.floor(baseResolutionWidth / 2 - unicode.len(text) / 2), math.floor(baseResolutionHeight / 2), text) - - GPUProxy.bind(mainScreenAddress, false) - - config.map[SSY] = config.map[SSY] or {} - config.map[SSY][SSX] = e2 - - SSX, hue = SSX + 1, hue + hueStep - if SSX > hSlider.value then - SSX, SSY = 1, SSY + 1 - if SSY > vSlider.value then - saveConfig() - break - end - end - - application:draw() - end - end - - GUI.alert("All screens has been successfully calibrated") - mainMenu() - else - GUI.alert("Invalid count of connected screens. You're specified " .. specifiedCount .. " of screens, but there's " .. connectedCount .. " connected screens") - end - end - - application:draw() - end - end - - actionComboBox.onItemSelected() - application:draw(force) -end - --------------------------------------------------------------------------------- - -mainMenu(true) -application:start() \ No newline at end of file diff --git a/Applications/Nano/About/English.txt b/Applications/Nano/About/English.txt deleted file mode 100644 index d3d8e1be..00000000 --- a/Applications/Nano/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Программа для работы с нанороботами. Для ее работы требуется беспроводная карта, чтобы отсылать роботам сообщения, а также вы должны стоять практически вплотную к системному блоку компьютера. \ No newline at end of file diff --git a/Applications/Nano/About/Russian.txt b/Applications/Nano/About/Russian.txt deleted file mode 100644 index d3d8e1be..00000000 --- a/Applications/Nano/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Программа для работы с нанороботами. Для ее работы требуется беспроводная карта, чтобы отсылать роботам сообщения, а также вы должны стоять практически вплотную к системному блоку компьютера. \ No newline at end of file diff --git a/Applications/Nano/Icon.pic b/Applications/Nano/Icon.pic deleted file mode 100644 index c93426f3f1a4f51ce750a4e2d3f21a8b5c9ba0c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 159 zcmXZUu@S;R3$@nN^nZZGou+t1Rp7+69Q6F7w8tg01&pbo0aY!_r=xp2B9t& z=ed!LD9oZ$sR2Q-R%?VgpvC#yh)24mgHIO+?a_dvX`Q$A?RX}S_?siQ@yVJ_UAFag RKpq+9$ld*stK)iRDL;;U8oK}h diff --git a/Applications/Nano/Nano.lua b/Applications/Nano/Nano.lua deleted file mode 100644 index 7060f3dc..00000000 --- a/Applications/Nano/Nano.lua +++ /dev/null @@ -1,166 +0,0 @@ -local component = require("component") -local event = require("event") -local image = require("image") -local ecs = require("ECSAPI") - -if not component.isAvailable("modem") then error("Этой программе требуется плата беспроводной сети."); return end - -local modem = component.modem -local port1 = 1 -modem.open(port1) - -modem.broadcast(1, "nanomachines", "setResponsePort", 1) - -local oldX, oldY = gpu.getResolution() -gpu.setResolution(80, 25) -ecs.prepareToExit(0xFFFFFF) - -local name, minHealth, maxHealth, minHunger, maxHunger, minPower, maxPower, experience -local effects = "Эффекты: недоступно" - -local timing = 1 - -local contacts = { -} -for i = 1, 18 do table.insert(contacts, {false}) end -local scan = {} -local toggle = {} -local exit = {} - -local function listener(_, _, _, _, _, header, command, ...) - if header ~= "nanomachines" then return end - local data = { ... } - if command == "name" then - name = data[1] - elseif command == "age" then - age = data[1] - elseif command == "experience" then - experience = data[1] - elseif command == "health" then - minHealth = data[1] - maxHealth = data[2] - elseif command == "hunger" then - minHunger = data[1] - maxHunger = data[2] - elseif command == "power" then - minPower = data[1] - maxPower = data[2] - elseif command == "effects" then - local cyka = data[1] - effects = "Эффекты: " .. cyka - end -end -event.listen("modem_message", listener) - -local xFace, yFace = 14, 2 -local xInfo, yInfo = xFace + 16 + 2, yFace -local imageCyka = image.load("MineOS/System/OS/Icons/Steve.pic") -image.draw(xFace, yFace, imageCyka) - -local function request() - local oldPixels = ecs.info("auto", "auto", " ", "Осуществляется запрос к нанороботам. Ждите.") - modem.broadcast(port1, "nanomachines", "getName"); os.sleep(timing) - --modem.broadcast(port1, "nanomachines", "getAge"); os.sleep(timing) - modem.broadcast(port1, "nanomachines", "getExperience"); os.sleep(timing) - modem.broadcast(port1, "nanomachines", "getHealth"); os.sleep(timing) - modem.broadcast(port1, "nanomachines", "getHunger"); os.sleep(timing) - modem.broadcast(port1, "nanomachines", "getPowerState"); os.sleep(timing) - modem.broadcast(port1, "nanomachines", "getActiveEffects"); os.sleep(timing) - ecs.drawOldPixels(oldPixels) -end - -local function redraw(x, y) - local xPos, yPos = x, y - local width = 35 - ecs.square(xPos, yPos, 80, 8, 0xFFFFFF) - ecs.smartText(xPos, yPos, "§fИмя: §8" .. (name or "недоступно")); yPos = yPos + 1 - ecs.smartText(xPos, yPos, "§fОпыт: §8" .. (experience or "недоступно")); yPos = yPos + 2 - ecs.progressBar(xPos, yPos, width, 1, 0xCCCCCC, ecs.colors.red, math.floor((minHealth or 0) / (maxHealth or 1) * 100)) - ecs.adaptiveText(xPos + math.floor(width / 2) - 4, yPos, "Здоровье", 0xFFFFFF); yPos = yPos + 2 - - if (maxHunger or 1) < 20 then maxHunger = 20 end - ecs.progressBar(xPos, yPos, width, 1, 0xCCCCCC, ecs.colors.green, math.floor((minHunger or 0) / (maxHunger or 1) * 100)) - ecs.adaptiveText(xPos + math.floor(width / 2) - 3, yPos, "Голод", 0xFFFFFF); yPos = yPos + 2 - ecs.progressBar(xPos, yPos, width, 1, 0xCCCCCC, ecs.colors.blue, math.floor((minPower or 0) / (maxPower or 1) * 100)) - ecs.adaptiveText(xPos + math.floor(width / 2) - 8, yPos, "Заряд нанороботов", 0xFFFFFF); yPos = yPos + 2 - - xPos, yPos = xFace, yFace + 9 - ecs.separator(1, yPos, 80, 0xFFFFFF, 0xCCCCCC) - yPos = yPos + 1 - - if effects then - gpu.setForeground(0x000000) - ecs.centerText("x", yPos, string.rep(" ", 40) .. effects .. string.rep(" ", 40)) - yPos = yPos + 1 - end - yPos = yPos + 1 - - xPos = 14 - for i = 1, #contacts do - contacts[i][2], contacts[i][3], contacts[i][4], contacts[i][5] = ecs.drawButton(xPos, yPos, 5, 3, tostring(i), (function() if contacts[i][1] then return ecs.colors.blue else return 0xBBBBBB end end)(), 0xFFFFFF) - xPos = contacts[i][4] + 2 - if i == 9 then yPos = yPos + 4; xPos = 14 end - end - yPos = yPos + 4 - xPos = 14 - scan = {ecs.drawAdaptiveButton(xPos, yPos, 2, 1, "Сканирование", 0x444444, 0xFFFFFF)} - toggle = {ecs.drawAdaptiveButton(scan[3] + 3, yPos, 2, 1, "Переключить контакты", 0x444444, 0xFFFFFF)} - exit = {ecs.drawAdaptiveButton(toggle[3] + 3, yPos, 2, 1, "Выйти", 0x444444, 0xFFFFFF)} - -end - -local function switchContacts() - os.sleep(timing) - for i = 1, #contacts do - ecs.info("auto", "auto", " ", "Переключаю "..i.." контакт") - modem.broadcast(port1, "nanomachines", "setInput", i, contacts[i][1]); os.sleep(timing) - end -end - -redraw(xInfo, yInfo) -request() -redraw(xInfo, yInfo) - -while true do - local e = {event.pull("touch")} - for i = 1, #contacts do - if ecs.clickedAtArea(e[3], e[4], contacts[i][2], contacts[i][3], contacts[i][4], contacts[i][5]) then - contacts[i][1] = not contacts[i][1] - redraw(xInfo, yInfo) - break - end - end - - if ecs.clickedAtArea(e[3], e[4], scan[1], scan[2], scan[3], scan[4]) then - ecs.drawAdaptiveButton(scan[1], scan[2], 2, 1, "Сканирование", ecs.colors.red, 0xFFFFFF) - --os.sleep(0.3) - request() - ecs.prepareToExit(0xFFFFFF) - redraw(xInfo, yInfo) - image.draw(xFace, yFace, imageCyka) - elseif ecs.clickedAtArea(e[3], e[4], toggle[1], toggle[2], toggle[3], toggle[4]) then - ecs.drawAdaptiveButton(toggle[1], toggle[2], 2, 1, "Переключить контакты", ecs.colors.red, 0xFFFFFF) - --os.sleep(0.3) - switchContacts() - ecs.prepareToExit(0xFFFFFF) - redraw(xInfo, yInfo) - image.draw(xFace, yFace, imageCyka) - elseif ecs.clickedAtArea(e[3], e[4], exit[1], exit[2], exit[3], exit[4]) then - ecs.drawAdaptiveButton(exit[1], exit[2], 2, 1, "Выйти", ecs.colors.red, 0xFFFFFF) - os.sleep(0.3) - event.ignore("modem_message", listener) - gpu.setResolution(oldX, oldY) - ecs.prepareToExit() - return - end - -end - -event.ignore("modem_message", listener) - - - - - - - diff --git a/Applications/Nanomachines/Icon.pic b/Applications/Nanomachines.app/Icon.pic similarity index 100% rename from Applications/Nanomachines/Icon.pic rename to Applications/Nanomachines.app/Icon.pic diff --git a/Applications/Nanomachines/Localizations/English.lang b/Applications/Nanomachines.app/Localizations/English.lang similarity index 100% rename from Applications/Nanomachines/Localizations/English.lang rename to Applications/Nanomachines.app/Localizations/English.lang diff --git a/Applications/Nanomachines/Localizations/French.lang b/Applications/Nanomachines.app/Localizations/French.lang similarity index 100% rename from Applications/Nanomachines/Localizations/French.lang rename to Applications/Nanomachines.app/Localizations/French.lang diff --git a/Applications/Nanomachines/Localizations/Russian.lang b/Applications/Nanomachines.app/Localizations/Russian.lang similarity index 100% rename from Applications/Nanomachines/Localizations/Russian.lang rename to Applications/Nanomachines.app/Localizations/Russian.lang diff --git a/Applications/Nanomachines/Localizations/Ukrainian.lang b/Applications/Nanomachines.app/Localizations/Ukrainian.lang similarity index 100% rename from Applications/Nanomachines/Localizations/Ukrainian.lang rename to Applications/Nanomachines.app/Localizations/Ukrainian.lang diff --git a/Applications/Nanomachines/Main.lua b/Applications/Nanomachines.app/Main.lua similarity index 89% rename from Applications/Nanomachines/Main.lua rename to Applications/Nanomachines.app/Main.lua index 7605c7de..12c955de 100644 --- a/Applications/Nanomachines/Main.lua +++ b/Applications/Nanomachines.app/Main.lua @@ -1,15 +1,13 @@ -local computer = require("computer") -local component = require("component") local GUI = require("GUI") -local buffer = require("doubleBuffering") -local fs = require("filesystem") -local color = require("color") -local image = require("image") -local unicode = require("unicode") -local MineOSInterface = require("MineOSInterface") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") +local screen = require("Screen") +local fs = require("Filesystem") +local color = require("Color") +local image = require("Image") +local system = require("System") +local paths = require("Paths") +local system = require("System") +local text = require("Text") -------------------------------------------------------------------------------- @@ -25,24 +23,24 @@ local historyLimit = 30 local modem local currentEffects = "{}" -local localization = MineOSCore.getCurrentScriptLocalization() +local localization = system.getCurrentScriptLocalization() local config = { favourites = {}, } -local configPath = MineOSPaths.applicationData .. "Nanomachines/Config.cfg" +local configPath = paths.user.applicationData .. "Nanomachines/Config.cfg" if fs.exists(configPath) then - config = table.fromFile(configPath) + config = filesystem.readTable(configPath) end local function saveConfig() - table.toFile(configPath, config) + filesystem.writeTable(configPath, config) end -------------------------------------------------------------------------------- -local application, window = MineOSInterface.addWindow(GUI.filledWindow(1, 1, 80, 22, 0xF0F0F0)) +local workspace, window = system.addWindow(GUI.filledWindow(1, 1, 80, 22, 0xF0F0F0)) local inputsPanel = window:addChild(GUI.panel(1, 1, 19, window.height, 0x2D2D2D)) window.backgroundPanel.localX = inputsPanel.width + 1 window.backgroundPanel.width = window.width - inputsPanel.width @@ -80,7 +78,7 @@ local function addMessage(prefix, ...) message[i] = tostring(message[i]) end - message = string.wrap(prefix .. table.concat(message, ", "), textBox.width - 2) + message = text.wrap(prefix .. table.concat(message, ", "), textBox.width - 2) for i = 1, #message do table.insert(textBox.lines, message[i]) @@ -95,7 +93,7 @@ end local function broadcast(...) addMessage(localization.sent .. ": ", ...) - application:draw() + workspace:draw() modem.broadcast(port, "nanomachines", ...) end @@ -138,7 +136,7 @@ for i = 1, maxInputs do local input = inputsContainer:addChild(GUI.switch(x, y, width, 0x66DB80, 0x1E1E1E, 0xE1E1E1, false)) input.onStateChanged = function() checkSwitches() - application:draw() + workspace:draw() broadcastPut("setInput", i, input.state) broadcastPut("getActiveEffects") @@ -203,8 +201,8 @@ local function updateEffects(variants) effectsLayout:removeChildren() local function effectDraw(object) - buffer.drawRectangle(object.x, object.y, object.width, object.height, object.backgroundColor, object.textColor, " ") - buffer.drawText(object.x + 1, object.y + 1, object.textColor, object.text) + screen.drawRectangle(object.x, object.y, object.width, object.height, object.backgroundColor, object.textColor, " ") + screen.drawText(object.x + 1, object.y + 1, object.textColor, object.text) end local step = false @@ -230,7 +228,7 @@ local function updateEffects(variants) effectsLayout.height = #effectsLayout.children * 3 end -local function runtimeEventHandler(application, object, e1, e2, e3, e4, e5, e6, e7, e8, ...) +local function runtimeEventHandler(workspace, object, e1, e2, e3, e4, e5, e6, e7, e8, ...) if e1 == "modem_message" and e6 == "nanomachines" then if e7 == "input" then local child = inputsContainer.children[e8] @@ -245,7 +243,7 @@ local function runtimeEventHandler(application, object, e1, e2, e3, e4, e5, e6, end addMessage(localization.received .. ": ", e7, e8, ...) - application:draw() + workspace:draw() broadcastNext() elseif e1 == "scroll" then @@ -259,7 +257,7 @@ local function runtimeEventHandler(application, object, e1, e2, e3, e4, e5, e6, cell.verticalMargin = to end - application:draw() + workspace:draw() end end @@ -342,7 +340,7 @@ favouritesComboBox.onItemSelected = function() end checkSwitches() - application:draw() + workspace:draw() broadcastNext() end @@ -357,19 +355,20 @@ end window.actionButtons:moveToFront() if component.isAvailable("modem") then - modem = component.modem + modem = component.get("modem") + if modem.isWireless() then modem.open(port) textBoxButton.onTouch() updateEffects(parseEffects()) syncReset() - layout.eventHandler = function(application, object, e1, e2, e3, e4, e5, e6, e7, e8, e9) + layout.eventHandler = function(workspace, object, e1, e2, e3, e4, e5, e6, e7, e8, e9) if not e1 then if computer.uptime() >= syncDeadline then syncReset() setLines(localization.syncInfo) - application:draw() + workspace:draw() end if not syncStarted then @@ -394,14 +393,14 @@ if component.isAvailable("modem") then inputsContainer.children[i]:setState(syncResult[i]) end checkSwitches() - application:draw() + workspace:draw() layout.eventHandler = runtimeEventHandler return end setLines(string.format(localization.syncProgress .. localization.syncContacts, #syncResult, maxInputs)) - application:draw() + workspace:draw() syncUpdate() broadcastNext() @@ -414,4 +413,4 @@ else setLines(localization.noModem) end -application:draw() \ No newline at end of file +workspace:draw() \ No newline at end of file diff --git a/Applications/Palette/Icon.pic b/Applications/Palette.app/Icon.pic similarity index 100% rename from Applications/Palette/Icon.pic rename to Applications/Palette.app/Icon.pic diff --git a/Applications/Palette.app/Main.lua b/Applications/Palette.app/Main.lua new file mode 100755 index 00000000..78323b0a --- /dev/null +++ b/Applications/Palette.app/Main.lua @@ -0,0 +1,11 @@ + +local GUI = require("GUI") +local system = require("System") + +local workspace, window = system.addWindow(GUI.palette(1, 1, 0x9900FF)) +window.submitButton.onTouch = function() + window:remove() + workspace:draw() +end + +window.cancelButton.onTouch = window.submitButton.onTouch \ No newline at end of file diff --git a/Applications/Palette/Palette.lua b/Applications/Palette/Palette.lua deleted file mode 100755 index 27c88733..00000000 --- a/Applications/Palette/Palette.lua +++ /dev/null @@ -1,11 +0,0 @@ - -local GUI = require("GUI") -local MineOSInterface = require("MineOSInterface") - -local application, window = MineOSInterface.addWindow(GUI.palette(1, 1, 0x9900FF)) -window.submitButton.onTouch = function() - window:close() - application:draw() -end - -window.cancelButton.onTouch = window.submitButton.onTouch \ No newline at end of file diff --git a/Applications/Photoshop/Icon.pic b/Applications/Photoshop/Icon.pic deleted file mode 100644 index a4e9df47df98c47b2b89e14d086fc8991dd5e3d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 161 zcmXAhu?@mN3`OtTKA#yP5^Wd*BqSOrE(@hX%s_L9k|CG^iTXC6P7!>GrLQMHefx4a z9n}OOSS=%Z%n)cTcm3i6ApI6tUVsi02x)_}!}=y8u|%zpYda=_GIYQWFK?ih+xk;l dYme-jT|eJzn recentFilesLimit then - table.remove(config.recentFiles, #config.recentFiles) - end - - saveConfig() -end - -loadConfig() - -local mainContainer = GUI.fullScreenContainer() - -mainContainer.menu = mainContainer:addChild(GUI.menu(1, 1, mainContainer.width, 0xE1E1E1, 0x5A5A5A, 0x3366CC, 0xFFFFFF, nil)) - -local function addTitle(container, text) - local titleContainer = container:addChild(GUI.container(1, 1, container.width, 1)) - titleContainer:addChild(GUI.panel(1, 1, titleContainer.width, 1, 0x2D2D2D)) - titleContainer:addChild(GUI.text(2, 1, 0xD2D2D2, text)) - - return titleContainer -end - -local pizdaWidth = 28 -mainContainer.sidebarPanel = mainContainer:addChild(GUI.panel(mainContainer.width - pizdaWidth + 1, 2, pizdaWidth, mainContainer.height - 1, 0x3C3C3C)) -mainContainer.sidebarLayout = mainContainer:addChild(GUI.layout(mainContainer.sidebarPanel.localX, 2, mainContainer.sidebarPanel.width, mainContainer.sidebarPanel.height, 1, 1)) -mainContainer.sidebarLayout:setCellAlignment(1, 1, GUI.alignment.horizontal.center, GUI.alignment.vertical.top) - -addTitle(mainContainer.sidebarLayout, "Recent colors") - -local recentColorsContainer = mainContainer.sidebarLayout:addChild(GUI.container(1, 1, mainContainer.sidebarLayout.width - 2, 4)) -local x, y = 1, 1 -for i = 1, #config.recentColors do - local button = recentColorsContainer:addChild(GUI.button(x, y, 2, 1, 0x0, 0x0, 0x0, 0x0, " ")) - button.onTouch = function() - mainContainer.primaryColorSelector.color = config.recentColors[i] - mainContainer:drawOnScreen() - end - - x = x + 2 - if x > recentColorsContainer.width - 1 then - x, y = 1, y + 1 - end -end - -local currentToolTitle = addTitle(mainContainer.sidebarLayout, "Tool properties") - -mainContainer.currentToolLayout = mainContainer.sidebarLayout:addChild(GUI.layout(1, 1, mainContainer.sidebarLayout.width, 1, 1, 1)) -mainContainer.currentToolLayout:setCellAlignment(1, 1, GUI.alignment.horizontal.center, GUI.alignment.vertical.top) -mainContainer.currentToolLayout:setCellFitting(1, 1, true, false, 2, 0) - -local aboutToolTitle = addTitle(mainContainer.sidebarLayout, "About tool") -local aboutToolTextBox = mainContainer.sidebarLayout:addChild(GUI.textBox(1, 1, mainContainer.sidebarLayout.width - 2, 1, nil, 0x787878, {}, 1, 0, 0)) - -mainContainer.toolsList = mainContainer:addChild(GUI.list(1, 2, 6, mainContainer.height - 1, 3, 0, 0x3C3C3C, 0xD2D2D2, 0x3C3C3C, 0xD2D2D2, 0x2D2D2D, 0xD2D2D2)) -mainContainer.backgroundPanel = mainContainer:addChild(GUI.panel(mainContainer.toolsList.width + 1, 2, mainContainer.width - mainContainer.toolsList.width - mainContainer.sidebarPanel.width, mainContainer.height - 1, 0x1E1E1E)) -mainContainer.image = mainContainer:addChild(GUI.object(1, 1, 1, 1)) -mainContainer.image.data = {} - -local function onToolTouch(index) - tool = mainContainer.toolsList.itemsLayout.children[index].tool - - mainContainer.toolsList:select(index) - mainContainer.currentToolOverlay:deleteChildren() - mainContainer.currentToolLayout:deleteChildren() - - currentToolTitle.hidden = not tool.onSelection - mainContainer.currentToolLayout.hidden = currentToolTitle.hidden - - if tool.onSelection then - local result, reason = pcall(tool.onSelection, mainContainer) - if result then - mainContainer.currentToolLayout:update() - local lastChild = mainContainer.currentToolLayout.children[#mainContainer.currentToolLayout.children] - if lastChild then - mainContainer.currentToolLayout.height = lastChild.localY + lastChild.height - 1 - end - else - GUI.error(reason) - end - end - - aboutToolTitle.hidden = not tool.about - aboutToolTextBox.hidden = aboutToolTitle.hidden - - if tool.about then - aboutToolTextBox.lines = string.wrap({tool.about}, aboutToolTextBox.width) - aboutToolTextBox.height = #aboutToolTextBox.lines - end - - mainContainer:drawOnScreen() -end - -local modules = fs.sortedList(toolsPath, "name", false) -for i = 1, #modules do - local result, reason = loadfile(toolsPath .. modules[i]) - if result then - result, reason = pcall(result) - if result then - local item = mainContainer.toolsList:addItem(reason.shortcut) - item.tool = reason - item.onTouch = function() - onToolTouch(i) - end - else - error("Failed to perform pcall() on module " .. modules[i] .. ": " .. reason) - end - else - error("Failed to perform loadfile() on module " .. modules[i] .. ": " .. reason) - end -end - -mainContainer.image.draw = function(object) - GUI.windowShadow(object.x, object.y, object.width, object.height, nil, true) - - local y, text = object.y + object.height + 1, "Size: " .. object.width .. "x" .. object.height - buffer.text(math.floor(object.x + object.width / 2 - unicode.len(text) / 2), y, 0x5A5A5A, text) - - if savePath then - text = "Path: " .. savePath - buffer.text(math.floor(object.x + object.width / 2 - unicode.len(text) / 2), y + 1, 0x5A5A5A, text) - end - - local x, y, step, notStep, background, foreground, symbol = object.x, object.y, false, mainContainer.image.width % 2 - for i = 3, #mainContainer.image.data, 4 do - - if mainContainer.image.data[i + 2] == 0 then - background = mainContainer.image.data[i] - foreground = mainContainer.image.data[i + 1] - symbol = mainContainer.image.data[i + 3] - elseif mainContainer.image.data[i + 2] < 1 then - background = color.blend(config.transparencyBackground, mainContainer.image.data[i], mainContainer.image.data[i + 2]) - foreground = mainContainer.image.data[i + 1] - symbol = mainContainer.image.data[i + 3] - else - if mainContainer.image.data[i + 3] == " " then - background = config.transparencyBackground - foreground = config.transparencyForeground - symbol = step and "▒" or "░" - else - background = config.transparencyBackground - foreground = mainContainer.image.data[i + 1] - symbol = mainContainer.image.data[i + 3] - - end - end - - buffer.set(x, y, background, foreground, symbol) - - x, step = x + 1, not step - if x > object.x + object.width - 1 then - x, y = object.x, y + 1 - if notStep == 0 then - step = not step - end - end - end -end - -local function updateRecentColorsButtons() - for i = 1, #config.recentColors do - recentColorsContainer.children[i].colors.default.background = config.recentColors[i] - recentColorsContainer.children[i].colors.pressed.background = 0xFFFFFF - config.recentColors[i] - end -end - -local function swapColors() - mainContainer.primaryColorSelector.color, mainContainer.secondaryColorSelector.color = mainContainer.secondaryColorSelector.color, mainContainer.primaryColorSelector.color - mainContainer:drawOnScreen() -end - -local function colorSelectorDraw(object) - buffer.square(object.x + 1, object.y, object.width - 2, object.height, object.color, 0x0, " ") - for y = object.y, object.y + object.height - 1 do - buffer.text(object.x, y, object.color, "⢸") - buffer.text(object.x + object.width - 1, y, object.color, "⡇") - end -end - -mainContainer.secondaryColorSelector = mainContainer:addChild(GUI.colorSelector(2, mainContainer.toolsList.height - 3, 5, 2, 0xFFFFFF, " ")) -mainContainer.primaryColorSelector = mainContainer:addChild(GUI.colorSelector(1, mainContainer.toolsList.height - 4, 5, 2, 0x880000, " ")) -mainContainer.secondaryColorSelector.draw, mainContainer.primaryColorSelector.draw = colorSelectorDraw, colorSelectorDraw - -mainContainer:addChild(GUI.adaptiveButton(3, mainContainer.secondaryColorSelector.localY + mainContainer.secondaryColorSelector.height + 1, 0, 0, nil, 0xD2D2D2, nil, 0xA5A5A5, "<>")).onTouch = swapColors - -mainContainer.image.eventHandler = function(mainContainer, object, e1, e2, e3, e4, ...) - if e1 == "key_down" then - -- D - if e4 == 32 then - mainContainer.primaryColorSelector.color, mainContainer.secondaryColorSelector.color = 0x0, 0xFFFFFF - mainContainer:drawOnScreen() - -- X - elseif e4 == 45 then - swapColors() - else - for i = 1, #mainContainer.toolsList.itemsLayout.children do - if e4 == mainContainer.toolsList.itemsLayout.children[i].tool.keyCode then - onToolTouch(i) - return - end - end - end - end - - local result, reason = pcall(tool.eventHandler, mainContainer, object, e1, e2, e3, e4, ...) - if not result then - GUI.error("Tool eventHandler() failed: " .. reason) - end -end - -mainContainer.image.reposition = function() - mainContainer.image.width, mainContainer.image.height = mainContainer.image.data[1], mainContainer.image.data[2] - if mainContainer.image.width <= mainContainer.backgroundPanel.width then - mainContainer.image.localX = math.floor(mainContainer.backgroundPanel.x + mainContainer.backgroundPanel.width / 2 - mainContainer.image.width / 2) - mainContainer.image.localY = math.floor(mainContainer.backgroundPanel.y + mainContainer.backgroundPanel.height / 2 - mainContainer.image.height / 2) - else - mainContainer.image.localX, mainContainer.image.localY = 9, 3 - end -end - -local function newNoGUI(width, height) - savePath = nil - mainContainer.image.data = {width, height} - mainContainer.image.reposition() - - for i = 1, width * height do - table.insert(mainContainer.image.data, 0x0) - table.insert(mainContainer.image.data, 0x0) - table.insert(mainContainer.image.data, 1) - table.insert(mainContainer.image.data, " ") - end -end - -local function new() - local container = MineOSInterface.addUniversalContainer(mainContainer, "New picture") - - local widthInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x696969, 0xE1E1E1, 0x2D2D2D, "51", "Width")) - local heightInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x696969, 0xE1E1E1, 0x2D2D2D, "19", "Height")) - - widthInput.validator = function(text) - return tonumber(text) - end - heightInput.validator = widthInput.validator - - container.panel.eventHandler = function(mainContainer, object, e1) - if e1 == "touch" then - newNoGUI(tonumber(widthInput.text), tonumber(heightInput.text)) - container:delete() - mainContainer:drawOnScreen() - end - end - - mainContainer:drawOnScreen() -end - -local function loadImage(path) - local result, reason = image.load(path) - if result then - savePath = path - addRecentFile(path) - mainContainer.image.data = result - mainContainer.image.reposition() - else - GUI.error(reason) - end -end - -local function saveImage(path) - local result, reason = image.save(path, mainContainer.image.data, 6) - if result then - savePath = path - addRecentFile(path) - else - GUI.error(reason) - end -end - -mainContainer.menu:addItem("PE", 0x00B6FF) - -local fileItem = mainContainer.menu:addItem("File") -fileItem.onTouch = function() - local menu = GUI.contextMenu(fileItem.x, fileItem.y + 1) - - menu:addItem("New").onTouch = new - - menu:addSeparator() - - menu:addItem("Open").onTouch = function() - local filesystemDialog = GUI.addFilesystemDialogToContainer(mainContainer, 50, math.floor(mainContainer.height * 0.8), true, "Open", "Cancel", "File name", "/") - filesystemDialog:setMode(GUI.filesystemModes.open, GUI.filesystemModes.file) - filesystemDialog:addExtensionFilter(".pic") - filesystemDialog:expandPath(MineOSPaths.desktop) - filesystemDialog:show() - - filesystemDialog.onSubmit = function(path) - loadImage(path) - mainContainer:drawOnScreen() - end - end - - local subMenu = menu:addSubMenu("Open recent", #config.recentFiles == 0) - for i = 1, #config.recentFiles do - subMenu:addItem(string.limit(config.recentFiles[i], 32, "left")).onTouch = function() - loadImage(config.recentFiles[i]) - mainContainer:drawOnScreen() - end - end - - menu:addItem("Open from URL").onTouch = function() - local container = MineOSInterface.addUniversalContainer(mainContainer, "Open from URL") - - local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x969696, 0xE1E1E1, 0x2D2D2D, "", "http://example.com/test.pic")) - input.onInputFinished = function() - if #input.text > 0 then - input:delete() - container.layout:addChild(GUI.label(1, 1, container.width, 1, 0x969696, "Downloading file..."):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top)) - mainContainer:drawOnScreen() - - local temporaryPath = MineOSCore.getTemporaryPath() .. ".pic" - local result, reason = web.download(input.text, temporaryPath) - - container:delete() - - if result then - loadImage(temporaryPath) - fs.remove(temporaryPath) - savePath = nil - else - GUI.error(reason) - end - - mainContainer:drawOnScreen() - end - end - - mainContainer:drawOnScreen() - end - - menu:addSeparator() - - menu:addItem("Save", not savePath).onTouch = function() - saveImage(savePath) - end - - menu:addItem("Save as").onTouch = function() - local filesystemDialog = GUI.addFilesystemDialogToContainer(mainContainer, 50, math.floor(mainContainer.height * 0.8), true, "Save", "Cancel", "File name", "/") - filesystemDialog:setMode(GUI.filesystemModes.save, GUI.filesystemModes.file) - filesystemDialog:addExtensionFilter(".pic") - filesystemDialog:expandPath(MineOSPaths.desktop) - filesystemDialog.filesystemTree.selectedItem = MineOSPaths.desktop - filesystemDialog:show() - - filesystemDialog.onSubmit = function(path) - saveImage(path) - end - end - - menu:addSeparator() - - menu:addItem("Exit").onTouch = function() - mainContainer:stopEventHandling() - end - - menu:show() -end - -mainContainer.menu:addItem("View").onTouch = function() - local container = MineOSInterface.addUniversalContainer(mainContainer, "View") - - local colorSelector1 = container.layout:addChild(GUI.colorSelector(1, 1, 36, 3, config.transparencyBackground, "Transparency background")) - local colorSelector2 = container.layout:addChild(GUI.colorSelector(1, 1, 36, 3, config.transparencyForeground, "Transparency foreground")) - - container.panel.eventHandler = function(mainContainer, object, e1) - if e1 == "touch" then - config.transparencyBackground, config.transparencyForeground = colorSelector1.color, colorSelector2.color - - container:delete() - mainContainer:drawOnScreen() - saveConfig() - end - end - - mainContainer:drawOnScreen() -end - -mainContainer.menu:addItem("Hotkeys").onTouch = function() - local container = MineOSInterface.addUniversalContainer(mainContainer, "Hotkeys") - local lines = { - "There are some hotkeys that works exactly like in real Photoshop:", - " ", - "M - selection tool", - "V - move tool", - "C - resizer tool", - "Alt - picker tool", - "B - brush tool", - "E - eraser tool", - "T - text tool", - "G - fill tool", - "F - braille tool", - " ", - "X - switch colors", - "D - make colors B/W", - } - - container.layout:addChild(GUI.textBox(1, 1, 36, 1, nil, 0x969696, lines, 1, 0, 0, true, true)).eventHandler = nil - mainContainer:drawOnScreen() -end - -mainContainer.currentToolOverlay = mainContainer:addChild(GUI.container(1, 1, mainContainer.width, mainContainer.height)) - ----------------------------------------------------------------- - -mainContainer.image:moveToBack() -mainContainer.backgroundPanel:moveToBack() - -updateRecentColorsButtons() -onToolTouch(5) - -if options.o or options.open and args[1] and fs.exists(args[1]) then - loadImage(args[1]) -else - newNoGUI(51, 19) -end - -mainContainer:drawOnScreen(true) -mainContainer:startEventHandling() \ No newline at end of file diff --git a/Applications/Photoshop/Tools/1.lua b/Applications/Photoshop/Tools/1.lua deleted file mode 100644 index fa26cd92..00000000 --- a/Applications/Photoshop/Tools/1.lua +++ /dev/null @@ -1,160 +0,0 @@ - -local GUI = require("GUI") -local buffer = require("doubleBuffering") -local image = require("image") -local tool = {} - ------------------------------------------------------- - -tool.shortcut = "Se" -tool.keyCode = 50 -tool.about = "Selection tool allows you to select preferred area on image and to perform some operations on it. Green dots mean start and end points (for example, it needs to line rasterization)" - -local selector, touchX, touchY, dragX, dragY = GUI.object(1, 1, 1, 1) - -local fillButton = GUI.roundedButton(1, 1, 36, 1, 0xE1E1E1, 0x2D2D2D, 0x2D2D2D, 0xE1E1E1, "Fill") -local outlineButton = GUI.roundedButton(1, 1, 36, 1, 0xE1E1E1, 0x2D2D2D, 0x2D2D2D, 0xE1E1E1, "Outline") -local rasterizeLineButton = GUI.roundedButton(1, 1, 36, 1, 0xE1E1E1, 0x2D2D2D, 0x2D2D2D, 0xE1E1E1, "Rasterize line") -local rasterizeEllipseButton = GUI.roundedButton(1, 1, 36, 1, 0xE1E1E1, 0x2D2D2D, 0x2D2D2D, 0xE1E1E1, "Rasterize ellipse") -local clearButton = GUI.roundedButton(1, 1, 36, 1, 0x696969, 0xE1E1E1, 0x2D2D2D, 0xE1E1E1, "Clear") -local cropButton = GUI.roundedButton(1, 1, 36, 1, 0x696969, 0xE1E1E1, 0x2D2D2D, 0xE1E1E1, "Crop") - -local function repositionSelector(mainContainer) - if dragX - touchX >= 0 then - selector.localX, selector.width = touchX, dragX - touchX + 1 - else - selector.localX, selector.width = dragX, touchX - dragX + 1 - end - - if dragY - touchY >= 0 then - selector.localY, selector.height = touchY, dragY - touchY + 1 - else - selector.localY, selector.height = dragY, touchY - dragY + 1 - end - - mainContainer:drawOnScreen() -end - -local function fitSelector(mainContainer) - touchX, touchY, dragX, dragY = mainContainer.image.localX, mainContainer.image.localY, mainContainer.image.localX + mainContainer.image.width - 1, mainContainer.image.localY + mainContainer.image.height - 1 - repositionSelector(mainContainer) -end - -tool.onSelection = function(mainContainer) - mainContainer.currentToolLayout:addChild(fillButton).onTouch = function() - for j = selector.y, selector.y + selector.height - 1 do - for i = selector.x, selector.x + selector.width - 1 do - image.set(mainContainer.image.data, i - mainContainer.image.x + 1, j - mainContainer.image.y + 1, mainContainer.primaryColorSelector.color, 0x0, 0, " ") - end - end - - mainContainer:drawOnScreen() - end - - mainContainer.currentToolLayout:addChild(outlineButton).onTouch = function() - local x1, y1 = selector.x - mainContainer.image.x + 1, selector.y - mainContainer.image.y + 1 - local x2, y2 = x1 + selector.width - 1, y1 + selector.height - 1 - - for x = x1, x2 do - image.set(mainContainer.image.data, x, y1, mainContainer.primaryColorSelector.color, 0x0, 0, " ") - image.set(mainContainer.image.data, x, y2, mainContainer.primaryColorSelector.color, 0x0, 0, " ") - end - - for y = y1 + 1, y2 - 1 do - image.set(mainContainer.image.data, x1, y, mainContainer.primaryColorSelector.color, 0x0, 0, " ") - image.set(mainContainer.image.data, x2, y, mainContainer.primaryColorSelector.color, 0x0, 0, " ") - end - - mainContainer:drawOnScreen() - end - - mainContainer.currentToolLayout:addChild(rasterizeLineButton).onTouch = function() - buffer.rasterizeLine( - touchX - mainContainer.image.x + 1, - touchY - mainContainer.image.y + 1, - dragX - mainContainer.image.x + 1, - dragY - mainContainer.image.y + 1, - function(x, y) - image.set(mainContainer.image.data, x, y, mainContainer.primaryColorSelector.color, 0x0, 0, " ") - end - ) - - mainContainer:drawOnScreen() - end - - mainContainer.currentToolLayout:addChild(rasterizeEllipseButton).onTouch = function() - local minX, minY, maxX, maxY = math.min(touchX, dragX), math.min(touchY, dragY), math.max(touchX, dragX), math.max(touchY, dragY) - local centerX, centerY = math.ceil(minX + (maxX - minX) / 2), math.ceil(minY + (maxY - minY) / 2) - - buffer.rasterizeEllipse( - centerX - mainContainer.image.x + 1, - centerY - mainContainer.image.y + 1, - maxX - centerX, - maxY - centerY, - function(x, y) - image.set(mainContainer.image.data, x, y, mainContainer.primaryColorSelector.color, 0x0, 0, " ") - end - ) - - mainContainer:drawOnScreen() - end - - mainContainer.currentToolLayout:addChild(clearButton).onTouch = function() - for j = selector.y, selector.y + selector.height - 1 do - for i = selector.x, selector.x + selector.width - 1 do - image.set(mainContainer.image.data, i - mainContainer.image.x + 1, j - mainContainer.image.y + 1, 0x0, 0x0, 1, " ") - end - end - - mainContainer:drawOnScreen() - end - - mainContainer.currentToolLayout:addChild(cropButton).onTouch = function() - mainContainer.image.data = image.crop(mainContainer.image.data, selector.x - mainContainer.image.x + 1, selector.y - mainContainer.image.y + 1, selector.width, selector.height) - mainContainer.image.reposition() - fitSelector(mainContainer) - end - - mainContainer.currentToolOverlay:addChild(selector) - fitSelector(mainContainer) -end - -tool.eventHandler = function(mainContainer, object, e1, e2, e3, e4) - if e1 == "touch" then - touchX, touchY, dragX, dragY = e3, e4, e3, e4 - repositionSelector(mainContainer) - elseif e1 == "drag" then - dragX, dragY = e3, e4 - repositionSelector(mainContainer) - end -end - -selector.eventHandler = tool.eventHandler -selector.draw = function() - local step = true - for x = selector.x + 1, selector.x + selector.width - 2 do - buffer.text(x, selector.y, step and 0xFFFFFF or 0x0, "━") - buffer.text(x, selector.y + selector.height - 1, step and 0xFFFFFF or 0x0, "━") - step = not step - end - - step = true - for y = selector.y + 1, selector.y + selector.height - 2 do - buffer.text(selector.x, y, step and 0xFFFFFF or 0x0, "┃") - buffer.text(selector.x + selector.width - 1, y, step and 0xFFFFFF or 0x0, "┃") - step = not step - end - - buffer.text(selector.x, selector.y, 0x0, "┏") - buffer.text(selector.x + selector.width - 1, selector.y + selector.height - 1, 0x0, "┛") - - buffer.text(selector.x + selector.width - 1, selector.y, 0x0, "┓") - buffer.text(selector.x, selector.y + selector.height - 1, 0x0, "┗") - - buffer.text(touchX, touchY, 0x66FF80, "⬤") - buffer.text(dragX, dragY, 0x66FF80, "⬤") -end - ------------------------------------------------------- - -return tool \ No newline at end of file diff --git a/Applications/Photoshop/Tools/2.lua b/Applications/Photoshop/Tools/2.lua deleted file mode 100644 index c51230c8..00000000 --- a/Applications/Photoshop/Tools/2.lua +++ /dev/null @@ -1,28 +0,0 @@ - -local image = require("image") -local tool = {} - ------------------------------------------------------- - -tool.shortcut = "Mv" -tool.keyCode = 47 -tool.about = "Move tool allows you to move image as you wish. But be careful: large images will take a time to shift and redraw. Hello, shitty GPUs!" - -local xOld, yOld -tool.eventHandler = function(mainContainer, object, e1, e2, e3, e4) - if e1 == "touch" then - xOld, yOld = e3, e4 - elseif e1 == "drag" and xOld and yOld then - mainContainer.image.localX = mainContainer.image.localX + (e3 - xOld) - mainContainer.image.localY = mainContainer.image.localY + (e4 - yOld) - xOld, yOld = e3, e4 - - mainContainer:drawOnScreen() - elseif e1 == "drop" then - xOld, yOld = nil, nil - end -end - ------------------------------------------------------- - -return tool \ No newline at end of file diff --git a/Applications/Photoshop/Tools/3.lua b/Applications/Photoshop/Tools/3.lua deleted file mode 100644 index 5bc77f24..00000000 --- a/Applications/Photoshop/Tools/3.lua +++ /dev/null @@ -1,120 +0,0 @@ - -local GUI = require("GUI") -local image = require("image") -local tool = {} - ------------------------------------------------------- - -tool.shortcut = "Re" -tool.keyCode = 46 -tool.about = "Resizer tool allows to change picture size in real time. You can specify preffered direction, input width and height modifiers and smart script will do the rest." - -local x, y, stepX, stepY, buttonWidth, buttonHeight, buttonCount, buttons, currentX, currentY = 1, 1, 2, 1, 7, 3, 3, {} - -local buttonsContainer = GUI.container(1, 1, (buttonWidth + stepX) * buttonCount - stepX, (buttonHeight + stepY) * buttonCount - stepY) -local buttonsLayout = GUI.layout(1, 1, buttonsContainer.width, buttonsContainer.height, 1, 1) -buttonsLayout:setCellAlignment(1, 1, GUI.alignment.horizontal.center, GUI.alignment.vertical.top) -buttonsLayout:addChild(buttonsContainer) - -local widthInput = GUI.input(1, 1, 1, 1, 0x2D2D2D, 0xC3C3C3, 0x5A5A5A, 0x2D2D2D, 0xD2D2D2, "", "Width") -local heightInput = GUI.input(1, 1, 1, 1, 0x2D2D2D, 0xC3C3C3, 0x5A5A5A, 0x2D2D2D, 0xD2D2D2, "", "Height") - -local expandButton = GUI.roundedButton(1, 1, 36, 1, 0x696969, 0xE1E1E1, 0x2D2D2D, 0xE1E1E1, "Expand") -local cropButton = GUI.roundedButton(1, 1, 36, 1, 0x696969, 0xE1E1E1, 0x2D2D2D, 0xE1E1E1, "Crop") - -expandButton.colors.disabled.background, expandButton.colors.disabled.text = 0x4B4B4B, 0x787878 -cropButton.colors = expandButton.colors - -local function try(x, y, symbol) - if buttons[y] and buttons[y][x] then - buttons[y][x].text = symbol - end -end - -local function set(x, y) - for i = 1, #buttonsContainer.children do - buttonsContainer.children[i].text = " " - end - - currentX, currentY = x, y - - try(x, y, "⬤") - try(x + 1, y, "▶") - try(x - 1, y, "◀") - try(x, y + 1, "▼") - try(x, y - 1, "▲") - try(x + 1, y + 1, "↘") - try(x + 1, y - 1, "↗") - try(x - 1, y + 1, "↙") - try(x - 1, y - 1, "↖") -end - -for j = 1, buttonCount do - buttons[j] = {} - for i = 1, buttonCount do - buttons[j][i] = buttonsContainer:addChild(GUI.button(x, y, buttonWidth, buttonHeight, 0x2D2D2D, 0xB4B4B4, 0x696969, 0xD2D2D2, " ")) - buttons[j][i].onTouch = function() - set(i, j) - buttons[j][i]:getFirstParent():drawOnScreen() - end - - x = x + buttonWidth + stepX - end - - x, y = 1, y + buttonHeight + stepY -end - -set(2, 2) - -tool.onSelection = function(mainContainer) - mainContainer.currentToolLayout:addChild(buttonsLayout) - mainContainer.currentToolLayout:addChild(widthInput) - mainContainer.currentToolLayout:addChild(heightInput) - mainContainer.currentToolLayout:addChild(expandButton) - mainContainer.currentToolLayout:addChild(cropButton) - - widthInput.onInputFinished = function() - expandButton.disabled = not widthInput.text:match("^%d+$") or not heightInput.text:match("^%d+$") - cropButton.disabled = expandButton.disabled - - mainContainer:drawOnScreen() - end - heightInput.onInputFinished = widthInput.onInputFinished - widthInput.onInputFinished() - - expandButton.onTouch = function() - local width, height = tonumber(widthInput.text), tonumber(heightInput.text) - - mainContainer.image.data = image.expand(mainContainer.image.data, - currentY > 1 and height or 0, - currentY < 3 and height or 0, - currentX > 1 and width or 0, - currentX < 3 and width or 0, - 0x0, 0x0, 1, " ") - - mainContainer.image.reposition() - mainContainer:drawOnScreen() - end - - cropButton.onTouch = function() - local width, height = tonumber(widthInput.text), tonumber(heightInput.text) - - mainContainer.image.data = image.crop(mainContainer.image.data, - currentX == 1 and 1 or width + 1, - currentY == 1 and 1 or height + 1, - (currentX == 1 or currentX == 3) and mainContainer.image.width - width or mainContainer.image.width - width * 2, - (currentY == 1 or currentY == 3) and mainContainer.image.height - height or mainContainer.image.height - height * 2 - ) - - mainContainer.image.reposition() - mainContainer:drawOnScreen() - end -end - -tool.eventHandler = function(mainContainer, object, e1) - -end - ------------------------------------------------------- - -return tool \ No newline at end of file diff --git a/Applications/Photoshop/Tools/4.lua b/Applications/Photoshop/Tools/4.lua deleted file mode 100644 index b335c392..00000000 --- a/Applications/Photoshop/Tools/4.lua +++ /dev/null @@ -1,40 +0,0 @@ - -local image = require("image") -local GUI = require("GUI") -local tool = {} - ------------------------------------------------------- - -tool.shortcut = "Pi" -tool.keyCode = 56 -tool.about = "Picker tool allows to select interested data from image as primary or secondary color. You can configure of what colors to pick." - -local pickBackgroundSwitch = GUI.switchAndLabel(1, 1, width, 6, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, "Pick background:", true) -local pickForegroundSwitch = GUI.switchAndLabel(1, 1, width, 6, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, "Pick foreground:", true) - -tool.onSelection = function(mainContainer) - mainContainer.currentToolLayout:addChild(pickBackgroundSwitch) - mainContainer.currentToolLayout:addChild(pickForegroundSwitch) -end - -tool.eventHandler = function(mainContainer, object, e1, e2, e3, e4) - if e1 == "touch" or e1 == "drag" then - local x, y = e3 - mainContainer.image.x + 1, e4 - mainContainer.image.y + 1 - - local background, foreground = image.get(mainContainer.image.data, x, y) - - if pickBackgroundSwitch.switch.state then - mainContainer.secondaryColorSelector.color = background - end - - if pickForegroundSwitch.switch.state then - mainContainer.primaryColorSelector.color = foreground - end - - mainContainer:drawOnScreen() - end -end - ------------------------------------------------------- - -return tool \ No newline at end of file diff --git a/Applications/Photoshop/Tools/5.lua b/Applications/Photoshop/Tools/5.lua deleted file mode 100644 index 758530b9..00000000 --- a/Applications/Photoshop/Tools/5.lua +++ /dev/null @@ -1,63 +0,0 @@ - -local unicode = require("unicode") -local image = require("image") -local GUI = require("GUI") -local keyboard = require("keyboard") -local tool = {} - ------------------------------------------------------- - -tool.shortcut = "Bs" -tool.keyCode = 48 -tool.about = "Classic brush tool to perform drawing with specified radius and transparency. You can configure of what data will be drawn. Also you can specify preferred symbol to draw with, otherwise whitespace will be used." - -local backgroundSwitch = GUI.switchAndLabel(1, 1, width, 6, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, "Draw background:", true) -local foregroundSwitch = GUI.switchAndLabel(1, 1, width, 6, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, "Draw foreground:", true) -local alphaSwitch = GUI.switchAndLabel(1, 1, width, 6, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, "Draw alpha:", true) -local symbolSwitch = GUI.switchAndLabel(1, 1, width, 6, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, "Draw symbol:", true) -local symbolInput = GUI.input(1, 1, width, 1, 0x2D2D2D, 0xC3C3C3, 0x5A5A5A, 0x2D2D2D, 0xD2D2D2, "", "Symbol to draw with") -symbolInput.onInputFinished = function() - symbolInput.text = unicode.sub(symbolInput.text, 1, 1) -end -local alphaSlider = GUI.slider(1, 1, width, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, 0, 255, 0, false, "Alpha value: ", "") -alphaSlider.roundValues = true -local radiusSlider = GUI.slider(1, 1, width, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, 1, 8, 1, false, "Radius: ", " px") -radiusSlider.height = 2 -radiusSlider.roundValues = true - -tool.onSelection = function(mainContainer) - mainContainer.currentToolLayout:addChild(backgroundSwitch) - mainContainer.currentToolLayout:addChild(foregroundSwitch) - mainContainer.currentToolLayout:addChild(alphaSwitch) - mainContainer.currentToolLayout:addChild(symbolSwitch) - mainContainer.currentToolLayout:addChild(symbolInput) - mainContainer.currentToolLayout:addChild(alphaSlider) - mainContainer.currentToolLayout:addChild(radiusSlider) -end - -tool.eventHandler = function(mainContainer, object, e1, e2, e3, e4) - if e1 == "touch" or e1 == "drag" then - local x, y = e3 - mainContainer.image.x + 1, e4 - mainContainer.image.y + 1 - local meow = math.floor(radiusSlider.value) - - for j = y - meow + 1, y + meow - 1 do - for i = x - meow + 1, x + meow - 1 do - if i >= 1 and i <= mainContainer.image.width and j >= 1 and j <= mainContainer.image.height then - local background, foreground, alpha, symbol = image.get(mainContainer.image.data, i, j) - image.set(mainContainer.image.data, i, j, - backgroundSwitch.switch.state and mainContainer.primaryColorSelector.color or background, - foregroundSwitch.switch.state and mainContainer.secondaryColorSelector.color or foreground, - alphaSwitch.switch.state and alphaSlider.value / 255 or alpha, - symbolSwitch.switch.state and (symbolInput.text == "" and " " or symbolInput.text) or symbol - ) - end - end - end - - mainContainer:drawOnScreen() - end -end - ------------------------------------------------------- - -return tool \ No newline at end of file diff --git a/Applications/Photoshop/Tools/6.lua b/Applications/Photoshop/Tools/6.lua deleted file mode 100644 index 17769b11..00000000 --- a/Applications/Photoshop/Tools/6.lua +++ /dev/null @@ -1,53 +0,0 @@ - -local GUI = require("GUI") -local image = require("image") -local tool = {} - ------------------------------------------------------- - -tool.shortcut = "Er" -tool.keyCode = 18 -tool.about = "Eraser tool will cleanup pixels just like brush tool. You can configure of what data is need to be erased" - -local backgroundSwitch = GUI.switchAndLabel(1, 1, width, 6, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, "Erase background:", true) -local foregroundSwitch = GUI.switchAndLabel(1, 1, width, 6, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, "Erase foreground:", true) -local alphaSwitch = GUI.switchAndLabel(1, 1, width, 6, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, "Erase alpha:", true) -local symbolSwitch = GUI.switchAndLabel(1, 1, width, 6, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, "Erase symbol:", true) -local radiusSlider = GUI.slider(1, 1, 1, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, 1, 8, 1, false, "Radius: ", " px") -radiusSlider.height = 2 -radiusSlider.roundValues = true - -tool.onSelection = function(mainContainer) - mainContainer.currentToolLayout:addChild(backgroundSwitch) - mainContainer.currentToolLayout:addChild(foregroundSwitch) - mainContainer.currentToolLayout:addChild(alphaSwitch) - mainContainer.currentToolLayout:addChild(symbolSwitch) - mainContainer.currentToolLayout:addChild(radiusSlider) -end - -tool.eventHandler = function(mainContainer, object, e1, e2, e3, e4) - if e1 == "touch" or e1 == "drag" then - local x, y = e3 - mainContainer.image.x + 1, e4 - mainContainer.image.y + 1 - local meow = math.floor(radiusSlider.value) - - for j = y - meow + 1, y + meow - 1 do - for i = x - meow + 1, x + meow - 1 do - if i >= 1 and i <= mainContainer.image.width and j >= 1 and j <= mainContainer.image.height then - local background, foreground, alpha, symbol = image.get(mainContainer.image.data, i, j) - image.set(mainContainer.image.data, i, j, - backgroundSwitch.switch.state and 0x0 or background, - foregroundSwitch.switch.state and 0x0 or foreground, - alphaSwitch.switch.state and 1 or alpha, - symbolSwitch.switch.state and " " or symbol - ) - end - end - end - - mainContainer:drawOnScreen() - end -end - ------------------------------------------------------- - -return tool \ No newline at end of file diff --git a/Applications/Photoshop/Tools/7.lua b/Applications/Photoshop/Tools/7.lua deleted file mode 100644 index 3ee6430b..00000000 --- a/Applications/Photoshop/Tools/7.lua +++ /dev/null @@ -1,52 +0,0 @@ - -local unicode = require("unicode") -local image = require("image") -local GUI = require("GUI") -local tool = {} - ------------------------------------------------------- - -tool.shortcut = "Tx" -tool.keyCode = 20 -tool.about = "Text tool allows you to type some text data with selected primary color right on your image! It's time to say \"ur mom gay\" to everyone <3" - -tool.eventHandler = function(mainContainer, object, e1, e2, e3, e4) - if e1 == "touch" then - local input = mainContainer:addChild(GUI.input( - e3 - 1, - e4, - mainContainer.image.x + mainContainer.image.width - e3 + 2, - 1, - nil, - mainContainer.primaryColorSelector.color, - mainContainer.primaryColorSelector.color, - nil, - mainContainer.primaryColorSelector.color, - "" - )) - - input.onInputFinished = function() - if #input.text > 0 then - local x, y = e3 - mainContainer.image.x + 1, e4 - mainContainer.image.y + 1 - for i = 1, unicode.len(input.text) do - if x <= mainContainer.image.width then - local background, foreground, alpha = image.get(mainContainer.image.data, x, y) - image.set(mainContainer.image.data, x, y, background, mainContainer.primaryColorSelector.color, alpha, unicode.sub(input.text, i, i)) - x = x + 1 - else - break - end - end - end - - input:delete() - mainContainer:drawOnScreen() - end - - input:startInput() - end -end - ------------------------------------------------------- - -return tool \ No newline at end of file diff --git a/Applications/Photoshop/Tools/8.lua b/Applications/Photoshop/Tools/8.lua deleted file mode 100644 index 620a57cc..00000000 --- a/Applications/Photoshop/Tools/8.lua +++ /dev/null @@ -1,44 +0,0 @@ - -local image = require("image") -local tool = {} - ------------------------------------------------------- - -tool.shortcut = "Fl" -tool.keyCode = 34 -tool.about = "Fill tool allows you to automatically fill areas with selected primary color just like in Paint. Oh God, where is my RAM...?" - -local function check(x, y, picture, sourceB, sourceF, sourceA, sourceS, newB, newF, newA, newS) - if x >= 1 and x <= picture[1] and y >= 1 and y <= picture[2] then - local currentB, currentF, currentA, currentS = image.get(picture, x, y) - if - currentB == sourceB - and - currentB ~= newB - then - image.set(picture, x, y, newB, newF, newA, newS) - return true - end - end -end - -local function pizda(x, y, picture, sourceB, sourceF, sourceA, sourceS, newB, newF, newA, newS) - if check(x, y - 1, picture, sourceB, sourceF, sourceA, sourceS, newB, newF, newA, newS) then pizda(x, y - 1, picture, sourceB, sourceF, sourceA, sourceS, newB, newF, newA, newS) end - if check(x + 1, y, picture, sourceB, sourceF, sourceA, sourceS, newB, newF, newA, newS) then pizda(x + 1, y, picture, sourceB, sourceF, sourceA, sourceS, newB, newF, newA, newS) end - if check(x, y + 1, picture, sourceB, sourceF, sourceA, sourceS, newB, newF, newA, newS) then pizda(x, y + 1, picture, sourceB, sourceF, sourceA, sourceS, newB, newF, newA, newS) end - if check(x - 1, y, picture, sourceB, sourceF, sourceA, sourceS, newB, newF, newA, newS) then pizda(x - 1, y, picture, sourceB, sourceF, sourceA, sourceS, newB, newF, newA, newS) end -end - -tool.eventHandler = function(mainContainer, object, e1, e2, e3, e4) - if e1 == "touch" then - local x, y = e3 - mainContainer.image.x + 1, e4 - mainContainer.image.y + 1 - local sourceB, sourceF, sourceA, sourceS = image.get(mainContainer.image.data, x, y) - pizda(x, y, mainContainer.image.data, sourceB, sourceF, sourceA, sourceS, mainContainer.primaryColorSelector.color, 0x0, 0, " ") - - mainContainer:drawOnScreen() - end -end - ------------------------------------------------------- - -return tool \ No newline at end of file diff --git a/Applications/Photoshop/Tools/9.lua b/Applications/Photoshop/Tools/9.lua deleted file mode 100644 index b29b05da..00000000 --- a/Applications/Photoshop/Tools/9.lua +++ /dev/null @@ -1,58 +0,0 @@ - -local image = require("image") -local GUI = require("GUI") -local tool = {} - ------------------------------------------------------- - -tool.shortcut = "Br" -tool.keyCode = 33 -tool.about = "Braille tool allows you to draw pixels with Braille symbols on your image. Select preferred mini-pixels via menu above, configure transparency affecting and \"Let's go fellas!\"" - -local layout = GUI.layout(1, 1, 1, 8, 1, 1) -local container, char, step = layout:addChild(GUI.container(1, 1, 8, 8)), " ", false -for y = 1, 8, 2 do - for x = 1, 8, 4 do - local button = container:addChild(GUI.button(x, y, 4, 2, step and 0xFFFFFF or 0xD2D2D2, 0x0, step and 0x0 or 0x1E1E1E, 0x0, " ")) - button.switchMode = true - button.onTouch = function() - local data = {} - for i = 1, #container.children do - data[i] = container.children[i].pressed and 1 or 0 - end - - char = string.brailleChar(table.unpack(data)) - end - - step = not step - end - - step = not step -end - -local backgroundSwitch = GUI.switchAndLabel(1, 1, 1, 6, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, "Draw background:", false) - -tool.onSelection = function(mainContainer) - mainContainer.currentToolLayout:addChild(layout) - mainContainer.currentToolLayout:addChild(backgroundSwitch) -end - -tool.eventHandler = function(mainContainer, object, e1, e2, e3, e4) - if e1 == "touch" or e1 == "drag" then - local x, y = e3 - mainContainer.image.x + 1, e4 - mainContainer.image.y + 1 - local background, foreground, alpha, symbol = image.get(mainContainer.image.data, x, y) - - image.set(mainContainer.image.data, x, y, - backgroundSwitch.switch.state and mainContainer.secondaryColorSelector.color or background, - mainContainer.primaryColorSelector.color, - backgroundSwitch.switch.state and 0 or alpha, - char - ) - - mainContainer:drawOnScreen() - end -end - ------------------------------------------------------- - -return tool \ No newline at end of file diff --git a/Applications/Piano/Icon.pic b/Applications/Piano/Icon.pic deleted file mode 100644 index cef5c6d7e152d03b5d24d407093a8cfcd57564ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99 zcmXBLu?>JQ5JSG(7F0gF+0!RU- ckW*_%gND6u{sMg2LY(+SgKV^L^dOvlf8wJH)Bpeg diff --git a/Applications/Piano/Piano.lua b/Applications/Piano/Piano.lua deleted file mode 100644 index 6305de74..00000000 --- a/Applications/Piano/Piano.lua +++ /dev/null @@ -1,144 +0,0 @@ -local c = require("component") -local event = require("event") -local gpu = c.gpu - -local xSize, ySize = gpu.getResolution() - -local colors = { - whiteKeyColor = 0xffffff, - blackKeyColor = 0x000000, - background = 0x262626, -} - -local sizes = { - widthOfWhiteKey = 3, - heightOfWhiteKey = 10, - widthOfBlackKey = 3, - heightOfBlackKey = 5, - doubleKeyBlockConst = 7, -} -sizes.yStartOfKeys = ySize - sizes.heightOfWhiteKey + 1 -sizes.xStartOfKeys = 3 -sizes.countOfKeysOnScreen = 0 - -local frequencyMultiplyer -local minFrequency, maxFrequency = 20, 2000 - ------------------------------------------------------------- - ---СОЗДАНИЕ ОБЪЕКТОВ -local obj = {} -local function newObj(class, name, ...) - obj[class] = obj[class] or {} - obj[class][name] = {...} -end - -local function calculateFrequencyMultiplyer() - local difference = maxFrequency - minFrequency - frequencyMultiplyer = difference / sizes.countOfKeysOnScreen -end - -local function drawKeysBlock(x, y, countOfWhite, countOfBlack) - local xPos = x - for i = 1, countOfWhite do - ecs.square(xPos, y, sizes.widthOfWhiteKey, sizes.heightOfWhiteKey, colors.whiteKeyColor) - - sizes.countOfKeysOnScreen = sizes.countOfKeysOnScreen + 1 - - newObj("Keys", sizes.countOfKeysOnScreen, xPos, y + sizes.heightOfBlackKey, xPos + sizes.widthOfWhiteKey - 1, y + sizes.heightOfWhiteKey - 1, true) - - xPos = xPos + sizes.widthOfWhiteKey + 1 - end - xPos = x + 2 - for i = 1, countOfBlack do - ecs.square(xPos, y, sizes.widthOfBlackKey, sizes.heightOfBlackKey, colors.blackKeyColor) - - newObj("Keys", sizes.countOfKeysOnScreen + 1, xPos, y, xPos + sizes.widthOfBlackKey - 1, y + sizes.heightOfBlackKey - 1, false) - - sizes.countOfKeysOnScreen = sizes.countOfKeysOnScreen + 1 - - xPos = xPos + sizes.widthOfBlackKey + 1 - end -end - -local function drawTwoKeysBlock(x, y) - drawKeysBlock(x, y, 3, 2) -end - -local function drawThreeKeysBlock(x, y) - drawKeysBlock(x, y, 4, 3) -end - -local function drawDoubleKeyBlock(x, y) - sizes.countOfKeysOnScreen = 0 - local xPos = x - for i = 1, 3 do - drawTwoKeysBlock(xPos, y) - xPos = xPos + 3 * (sizes.widthOfWhiteKey + 1) - drawThreeKeysBlock(xPos, y) - xPos = xPos + 4 * (sizes.widthOfWhiteKey + 1) - end - drawTwoKeysBlock(xPos, y) - - calculateFrequencyMultiplyer() -end - -local function setFrequencyToKeys() - calculateFrequencyMultiplyer() - local justBecomeBlack = false - local whiteCounter = 0 - local currentWhiteFrequency, currentBlackFrequency = minFrequency, minFrequency + frequencyMultiplyer - for i = 1, #obj["Keys"] do - if obj["Keys"][i][5] then - whiteCounter = whiteCounter + 1 - justBecomeBlack = true - currentWhiteFrequency = currentWhiteFrequency + frequencyMultiplyer - obj["Keys"][i][6] = currentWhiteFrequency - else - if justBecomeBlack then currentBlackFrequency = currentWhiteFrequency - frequencyMultiplyer * (whiteCounter - 1); justBecomeBlack = false end - currentBlackFrequency = currentBlackFrequency + frequencyMultiplyer - obj["Keys"][i][6] = currentBlackFrequency - whiteCounter = 0 - end - end -end - -local function drawAll() - ecs.clearScreen(colors.background) - drawDoubleKeyBlock(sizes.xStartOfKeys, sizes.yStartOfKeys) -end - -drawAll() -setFrequencyToKeys() - -while true do - local e = {event.pull()} - if e[1] == "touch" then - for key, val in pairs(obj["Keys"]) do - if ecs.clickedAtArea(e[3], e[4], obj["Keys"][key][1], obj["Keys"][key][2], obj["Keys"][key][3], obj["Keys"][key][4]) then - if obj["Keys"][key][5] then - ecs.square(obj["Keys"][key][1], obj["Keys"][key][2], sizes.widthOfWhiteKey, sizes.heightOfWhiteKey - sizes.heightOfBlackKey, ecs.colors.green) - else - ecs.square(obj["Keys"][key][1], obj["Keys"][key][2], sizes.widthOfBlackKey, sizes.heightOfBlackKey, ecs.colors.green) - end - - c.computer.beep(obj["Keys"][key][6]) - - if obj["Keys"][key][5] then - ecs.square(obj["Keys"][key][1], obj["Keys"][key][2], sizes.widthOfWhiteKey, sizes.heightOfWhiteKey - sizes.heightOfBlackKey, colors.whiteKeyColor) - else - ecs.square(obj["Keys"][key][1], obj["Keys"][key][2], sizes.widthOfBlackKey, sizes.heightOfBlackKey, colors.blackKeyColor) - end - - break - end - end - end -end - - - - - - - diff --git a/Applications/PictureEdit/Icon.pic b/Applications/Picture Edit.app/Icon.pic similarity index 100% rename from Applications/PictureEdit/Icon.pic rename to Applications/Picture Edit.app/Icon.pic diff --git a/Applications/Picture Edit.app/Main.lua b/Applications/Picture Edit.app/Main.lua new file mode 100644 index 00000000..234eb44d --- /dev/null +++ b/Applications/Picture Edit.app/Main.lua @@ -0,0 +1,501 @@ + +local GUI = require("GUI") +local internet = require("Internet") +local system = require("System") +local filesystem = require("Filesystem") +local image = require("Image") +local color = require("Color") +local screen = require("Screen") +local paths = require("Paths") +local text = require("Text") + +local args, options = system.parseArguments(...) + +-------------------------------------------------------------------- + +local recentColorsLimit = 52 +local recentFilesLimit = 10 + +local config = { + recentColors = {}, + recentFiles = {}, + transparencyBackground = 0xFFFFFF, + transparencyForeground = 0xD2D2D2, +} + +local currentScriptDirectory = filesystem.path(system.getCurrentScript()) +local toolsPath = currentScriptDirectory .. "Tools/" +local configPath = paths.user.applicationData .. "Picture Edit/Config2.cfg" +local savePath +local saveItem +local tool + +-------------------------------------------------------------------- + +local function saveConfig() + filesystem.writeTable(configPath, config) +end + +local function loadConfig() + if filesystem.exists(configPath) then + config = filesystem.readTable(configPath) + else + local perLine = 13 + local value, step = 0, 360 / (recentColorsLimit - perLine) + for i = 1, recentColorsLimit - perLine do + table.insert(config.recentColors, color.HSBToInteger(value, 1, 1)) + value = value + step + end + + value, step = 0, 255 / perLine + for i = 1, perLine do + table.insert(config.recentColors, color.RGBToInteger(math.floor(value), math.floor(value), math.floor(value))) + value = value + step + end + + saveConfig() + end +end + +local function addRecentFile(path) + for i = 1, #config.recentFiles do + if config.recentFiles[i] == path then + return + end + end + + table.insert(config.recentFiles, 1, path) + if #config.recentFiles > recentFilesLimit then + table.remove(config.recentFiles, #config.recentFiles) + end + + saveConfig() +end + +loadConfig() + +local workspace = GUI.workspace() + +workspace.menu = workspace:addChild(GUI.menu(1, 1, workspace.width, 0xE1E1E1, 0x5A5A5A, 0x3366CC, 0xFFFFFF, nil)) + +local function addTitle(container, text) + local titleContainer = container:addChild(GUI.container(1, 1, container.width, 1)) + titleContainer:addChild(GUI.panel(1, 1, titleContainer.width, 1, 0x2D2D2D)) + titleContainer:addChild(GUI.text(2, 1, 0xD2D2D2, text)) + + return titleContainer +end + +local pizdaWidth = 28 +workspace.sidebarPanel = workspace:addChild(GUI.panel(workspace.width - pizdaWidth + 1, 2, pizdaWidth, workspace.height - 1, 0x3C3C3C)) +workspace.sidebarLayout = workspace:addChild(GUI.layout(workspace.sidebarPanel.localX, 2, workspace.sidebarPanel.width, workspace.sidebarPanel.height, 1, 1)) +workspace.sidebarLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + +addTitle(workspace.sidebarLayout, "Recent colors") + +local recentColorsContainer = workspace.sidebarLayout:addChild(GUI.container(1, 1, workspace.sidebarLayout.width - 2, 4)) +local x, y = 1, 1 +for i = 1, #config.recentColors do + local button = recentColorsContainer:addChild(GUI.button(x, y, 2, 1, 0x0, 0x0, 0x0, 0x0, " ")) + button.onTouch = function() + workspace.primaryColorSelector.color = config.recentColors[i] + workspace:draw() + end + + x = x + 2 + if x > recentColorsContainer.width - 1 then + x, y = 1, y + 1 + end +end + +local currentToolTitle = addTitle(workspace.sidebarLayout, "Tool properties") + +workspace.currentToolLayout = workspace.sidebarLayout:addChild(GUI.layout(1, 1, workspace.sidebarLayout.width, 1, 1, 1)) +workspace.currentToolLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) +workspace.currentToolLayout:setFitting(1, 1, true, false, 2, 0) + +local aboutToolTitle = addTitle(workspace.sidebarLayout, "About tool") +local aboutToolTextBox = workspace.sidebarLayout:addChild(GUI.textBox(1, 1, workspace.sidebarLayout.width - 2, 1, nil, 0x787878, {}, 1, 0, 0)) + +workspace.toolsList = workspace:addChild(GUI.list(1, 2, 6, workspace.height - 1, 3, 0, 0x3C3C3C, 0xD2D2D2, 0x3C3C3C, 0xD2D2D2, 0x2D2D2D, 0xD2D2D2)) +workspace.backgroundPanel = workspace:addChild(GUI.panel(workspace.toolsList.width + 1, 2, workspace.width - workspace.toolsList.width - workspace.sidebarPanel.width, workspace.height - 1, 0x1E1E1E)) +workspace.image = workspace:addChild(GUI.object(1, 1, 1, 1)) +workspace.image.data = {} + +local function onToolTouch(index) + tool = workspace.toolsList:getItem(index).tool + + workspace.toolsList.selectedItem = index + workspace.currentToolOverlay:removeChildren() + workspace.currentToolLayout:removeChildren() + + currentToolTitle.hidden = not tool.onSelection + workspace.currentToolLayout.hidden = currentToolTitle.hidden + + if tool.onSelection then + local result, reason = pcall(tool.onSelection, workspace) + if result then + workspace.currentToolLayout:update() + local lastChild = workspace.currentToolLayout.children[#workspace.currentToolLayout.children] + if lastChild then + workspace.currentToolLayout.height = lastChild.localY + lastChild.height - 1 + end + else + GUI.alert(reason) + end + end + + aboutToolTitle.hidden = not tool.about + aboutToolTextBox.hidden = aboutToolTitle.hidden + + if tool.about then + aboutToolTextBox.lines = text.wrap({tool.about}, aboutToolTextBox.width) + aboutToolTextBox.height = #aboutToolTextBox.lines + end + + workspace:draw() +end + +local modules = filesystem.list(toolsPath) +for i = 1, #modules do + if filesystem.extension(modules[i]) == ".lua" then + local result, reason = loadfile(toolsPath .. modules[i]) + if result then + result, reason = pcall(result) + if result then + local item = workspace.toolsList:addItem(reason.shortcut) + item.tool = reason + item.onTouch = function() + onToolTouch(i) + end + else + error("Failed to perform pcall() on module " .. modules[i] .. ": " .. reason) + end + else + error("Failed to perform loadfile() on module " .. modules[i] .. ": " .. reason) + end + end +end + +workspace.image.draw = function(object) + GUI.drawShadow(object.x, object.y, object.width, object.height, nil, true) + + local y, text = object.y + object.height + 1, "Size: " .. object.width .. "x" .. object.height + screen.drawText(math.floor(object.x + object.width / 2 - unicode.len(text) / 2), y, 0x5A5A5A, text) + + if savePath then + text = "Path: " .. savePath + screen.drawText(math.floor(object.x + object.width / 2 - unicode.len(text) / 2), y + 1, 0x5A5A5A, text) + end + + local x, y, step, notStep, background, foreground, symbol = object.x, object.y, false, workspace.image.width % 2 + for i = 3, #workspace.image.data, 4 do + if workspace.image.data[i + 2] == 0 then + background = workspace.image.data[i] + foreground = workspace.image.data[i + 1] + symbol = workspace.image.data[i + 3] + elseif workspace.image.data[i + 2] < 1 then + background = color.blend(config.transparencyBackground, workspace.image.data[i], workspace.image.data[i + 2]) + foreground = workspace.image.data[i + 1] + symbol = workspace.image.data[i + 3] + else + if workspace.image.data[i + 3] == " " then + background = config.transparencyBackground + foreground = config.transparencyForeground + symbol = step and "▒" or "░" + else + background = config.transparencyBackground + foreground = workspace.image.data[i + 1] + symbol = workspace.image.data[i + 3] + end + end + + screen.set(x, y, background, foreground, symbol) + + x, step = x + 1, not step + if x > object.x + object.width - 1 then + x, y = object.x, y + 1 + if notStep == 0 then + step = not step + end + end + end +end + +local function updateRecentColorsButtons() + for i = 1, #config.recentColors do + recentColorsContainer.children[i].colors.default.background = config.recentColors[i] + recentColorsContainer.children[i].colors.pressed.background = 0xFFFFFF - config.recentColors[i] + end +end + +local function swapColors() + workspace.primaryColorSelector.color, workspace.secondaryColorSelector.color = workspace.secondaryColorSelector.color, workspace.primaryColorSelector.color + workspace:draw() +end + +local function colorSelectorDraw(object) + screen.drawRectangle(object.x + 1, object.y, object.width - 2, object.height, object.color, 0x0, " ") + for y = object.y, object.y + object.height - 1 do + screen.drawText(object.x, y, object.color, "⢸") + screen.drawText(object.x + object.width - 1, y, object.color, "⡇") + end +end + +workspace.secondaryColorSelector = workspace:addChild(GUI.colorSelector(2, workspace.toolsList.height - 3, 5, 2, 0xFFFFFF, " ")) +workspace.primaryColorSelector = workspace:addChild(GUI.colorSelector(1, workspace.toolsList.height - 4, 5, 2, 0x880000, " ")) +workspace.secondaryColorSelector.draw, workspace.primaryColorSelector.draw = colorSelectorDraw, colorSelectorDraw + +workspace:addChild(GUI.adaptiveButton(3, workspace.secondaryColorSelector.localY + workspace.secondaryColorSelector.height + 1, 0, 0, nil, 0xD2D2D2, nil, 0xA5A5A5, "<>")).onTouch = swapColors + +workspace.image.eventHandler = function(workspace, object, e1, e2, e3, e4, ...) + if e1 == "key_down" then + -- D + if e4 == 32 then + workspace.primaryColorSelector.color, workspace.secondaryColorSelector.color = 0x0, 0xFFFFFF + workspace:draw() + -- X + elseif e4 == 45 then + swapColors() + else + for i = 1, workspace.toolsList:count() do + if e4 == workspace.toolsList:getItem(i).tool.keyCode then + onToolTouch(i) + return + end + end + end + end + + local result, reason = pcall(tool.eventHandler, workspace, object, e1, e2, e3, e4, ...) + if not result then + GUI.alert("Tool eventHandler() failed: " .. reason) + end +end + +workspace.image.reposition = function() + workspace.image.width, workspace.image.height = workspace.image.data[1], workspace.image.data[2] + if workspace.image.width <= workspace.backgroundPanel.width then + workspace.image.localX = math.floor(workspace.backgroundPanel.x + workspace.backgroundPanel.width / 2 - workspace.image.width / 2) + workspace.image.localY = math.floor(workspace.backgroundPanel.y + workspace.backgroundPanel.height / 2 - workspace.image.height / 2) + else + workspace.image.localX, workspace.image.localY = 9, 3 + end +end + +local function newNoGUI(width, height) + savePath, saveItem.disabled = nil, true + workspace.image.data = {width, height} + workspace.image.reposition() + + for i = 1, width * height do + table.insert(workspace.image.data, 0x0) + table.insert(workspace.image.data, 0x0) + table.insert(workspace.image.data, 1) + table.insert(workspace.image.data, " ") + end +end + +local function new() + local container = GUI.addBackgroundContainer(workspace, true, true, "New picture") + + local widthInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x696969, 0xE1E1E1, 0x2D2D2D, "51", "Width")) + local heightInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x696969, 0xE1E1E1, 0x2D2D2D, "19", "Height")) + + widthInput.validator = function(text) + return tonumber(text) + end + heightInput.validator = widthInput.validator + + container.panel.eventHandler = function(workspace, object, e1) + if e1 == "touch" then + newNoGUI(tonumber(widthInput.text), tonumber(heightInput.text)) + container:remove() + workspace:draw() + end + end + + workspace:draw() +end + +local function loadImage(path) + local result, reason + + if filesystem.extension(path) == ".rawpic" then + result = image.fromString(filesystem.read(path)) + else + result, reason = image.load(path) + end + + if result then + savePath, saveItem.disabled = path, false + addRecentFile(path) + workspace.image.data = result + workspace.image.reposition() + else + GUI.alert(reason) + end +end + +local function saveImage(path) + if filesystem.extension(path) == ".pic" then + local result, reason = image.save(path, workspace.image.data, 6) + if result then + savePath, saveItem.disabled = path, false + + addRecentFile(path) + else + GUI.alert(reason) + end + else + savePath, saveItem.disabled = path, false + + filesystem.write(path, image.toString(workspace.image.data)) + end +end + +workspace.menu:addItem("PE", 0x00B6FF) + +local fileItem = workspace.menu:addContextMenu("File") +fileItem:addItem("New").onTouch = new + +fileItem:addSeparator() + +fileItem:addItem("Open").onTouch = function() + local filesystemDialog = GUI.addFilesystemDialog(workspace, true, 50, math.floor(workspace.height * 0.8), "Open", "Cancel", "File name", "/") + filesystemDialog:setMode(GUI.IO_MODE_OPEN, GUI.IO_MODE_FILE) + filesystemDialog:addExtensionFilter(".pic") + filesystemDialog:addExtensionFilter(".rawpic") + filesystemDialog:expandPath(paths.user.desktop) + filesystemDialog:show() + + filesystemDialog.onSubmit = function(path) + loadImage(path) + workspace:draw() + end +end + +local fileItemSubMenu = fileItem:addSubMenu("Open recent", #config.recentFiles == 0) +for i = 1, #config.recentFiles do + fileItemSubMenu:addItem(text.limit(config.recentFiles[i], 32, "left")).onTouch = function() + loadImage(config.recentFiles[i]) + workspace:draw() + end +end + +fileItem:addItem("Open from URL").onTouch = function() + local container = GUI.addBackgroundContainer(workspace, true, true, "Open from URL") + + local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x969696, 0xE1E1E1, 0x2D2D2D, "", "http://example.com/test.pic")) + input.onInputFinished = function() + if #input.text > 0 then + input:remove() + container.layout:addChild(GUI.label(1, 1, container.width, 1, 0x969696, "Downloading file..."):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)) + workspace:draw() + + local temporaryPath = system.getTemporaryPath() .. ".pic" + local result, reason = internet.download(input.text, temporaryPath) + + container:remove() + + if result then + loadImage(temporaryPath) + filesystem.remove(temporaryPath) + savePath, saveItem.disabled = nil, true + else + GUI.alert(reason) + end + + workspace:draw() + end + end + + workspace:draw() +end + +fileItem:addSeparator() + +saveItem = fileItem:addItem("Save") +saveItem.onTouch = function() + saveImage(savePath) +end + +fileItem:addItem("Save as").onTouch = function() + local filesystemDialog = GUI.addFilesystemDialog(workspace, true, 50, math.floor(workspace.height * 0.8), "Save", "Cancel", "File name", "/") + filesystemDialog:setMode(GUI.IO_MODE_SAVE, GUI.IO_MODE_FILE) + filesystemDialog:addExtensionFilter(".pic") + filesystemDialog:addExtensionFilter(".ocifstring") + filesystemDialog:expandPath(paths.user.desktop) + filesystemDialog.filesystemTree.selectedItem = paths.user.desktop + filesystemDialog:show() + + filesystemDialog.onSubmit = function(path) + saveImage(path) + end +end + +fileItem:addSeparator() + +fileItem:addItem("Exit").onTouch = function() + workspace:stop() +end + +workspace.menu:addItem("View").onTouch = function() + local container = GUI.addBackgroundContainer(workspace, true, true, "View") + + local colorSelector1 = container.layout:addChild(GUI.colorSelector(1, 1, 36, 3, config.transparencyBackground, "Transparency background")) + local colorSelector2 = container.layout:addChild(GUI.colorSelector(1, 1, 36, 3, config.transparencyForeground, "Transparency foreground")) + + container.panel.eventHandler = function(workspace, object, e1) + if e1 == "touch" then + config.transparencyBackground, config.transparencyForeground = colorSelector1.color, colorSelector2.color + + container:remove() + workspace:draw() + saveConfig() + end + end + + workspace:draw() +end + +workspace.menu:addItem("Hotkeys").onTouch = function() + local container = GUI.addBackgroundContainer(workspace, true, true, "Hotkeys") + local lines = { + "There are some hotkeys that works exactly like in real Photoshop:", + " ", + "M - selection tool", + "V - move tool", + "C - resizer tool", + "Alt - picker tool", + "B - brush tool", + "E - eraser tool", + "T - text tool", + "G - fill tool", + "F - braille tool", + " ", + "X - switch colors", + "D - make colors B/W", + } + + container.layout:addChild(GUI.textBox(1, 1, 36, 1, nil, 0x969696, lines, 1, 0, 0, true, true)).eventHandler = nil + workspace:draw() +end + +workspace.currentToolOverlay = workspace:addChild(GUI.container(1, 1, workspace.width, workspace.height)) + +---------------------------------------------------------------- + +workspace.image:moveToBack() +workspace.backgroundPanel:moveToBack() + +updateRecentColorsButtons() + +if options.o or options.open and args[1] and filesystem.exists(args[1]) then + loadImage(args[1]) +else + newNoGUI(51, 19) +end + +onToolTouch(5) +workspace:start() \ No newline at end of file diff --git a/Applications/Picture Edit.app/Tools/1.lua b/Applications/Picture Edit.app/Tools/1.lua new file mode 100644 index 00000000..470df5b1 --- /dev/null +++ b/Applications/Picture Edit.app/Tools/1.lua @@ -0,0 +1,160 @@ + +local GUI = require("GUI") +local screen = require("screen") +local image = require("image") +local tool = {} + +------------------------------------------------------ + +tool.shortcut = "Se" +tool.keyCode = 50 +tool.about = "Selection tool allows you to select preferred area on image and to perform some operations on it. Green dots mean start and end points (for example, it needs to line rasterization)" + +local selector, touchX, touchY, dragX, dragY = GUI.object(1, 1, 1, 1) + +local fillButton = GUI.roundedButton(1, 1, 36, 1, 0xE1E1E1, 0x2D2D2D, 0x2D2D2D, 0xE1E1E1, "Fill") +local outlineButton = GUI.roundedButton(1, 1, 36, 1, 0xE1E1E1, 0x2D2D2D, 0x2D2D2D, 0xE1E1E1, "Outline") +local rasterizeLineButton = GUI.roundedButton(1, 1, 36, 1, 0xE1E1E1, 0x2D2D2D, 0x2D2D2D, 0xE1E1E1, "Rasterize line") +local rasterizeEllipseButton = GUI.roundedButton(1, 1, 36, 1, 0xE1E1E1, 0x2D2D2D, 0x2D2D2D, 0xE1E1E1, "Rasterize ellipse") +local clearButton = GUI.roundedButton(1, 1, 36, 1, 0x696969, 0xE1E1E1, 0x2D2D2D, 0xE1E1E1, "Clear") +local cropButton = GUI.roundedButton(1, 1, 36, 1, 0x696969, 0xE1E1E1, 0x2D2D2D, 0xE1E1E1, "Crop") + +local function repositionSelector(workspace) + if dragX - touchX >= 0 then + selector.localX, selector.width = touchX, dragX - touchX + 1 + else + selector.localX, selector.width = dragX, touchX - dragX + 1 + end + + if dragY - touchY >= 0 then + selector.localY, selector.height = touchY, dragY - touchY + 1 + else + selector.localY, selector.height = dragY, touchY - dragY + 1 + end + + workspace:draw() +end + +local function fitSelector(workspace) + touchX, touchY, dragX, dragY = workspace.image.localX, workspace.image.localY, workspace.image.localX + workspace.image.width - 1, workspace.image.localY + workspace.image.height - 1 + repositionSelector(workspace) +end + +tool.onSelection = function(workspace) + workspace.currentToolLayout:addChild(fillButton).onTouch = function() + for j = selector.y, selector.y + selector.height - 1 do + for i = selector.x, selector.x + selector.width - 1 do + image.set(workspace.image.data, i - workspace.image.x + 1, j - workspace.image.y + 1, workspace.primaryColorSelector.color, 0x0, 0, " ") + end + end + + workspace:draw() + end + + workspace.currentToolLayout:addChild(outlineButton).onTouch = function() + local x1, y1 = selector.x - workspace.image.x + 1, selector.y - workspace.image.y + 1 + local x2, y2 = x1 + selector.width - 1, y1 + selector.height - 1 + + for x = x1, x2 do + image.set(workspace.image.data, x, y1, workspace.primaryColorSelector.color, 0x0, 0, " ") + image.set(workspace.image.data, x, y2, workspace.primaryColorSelector.color, 0x0, 0, " ") + end + + for y = y1 + 1, y2 - 1 do + image.set(workspace.image.data, x1, y, workspace.primaryColorSelector.color, 0x0, 0, " ") + image.set(workspace.image.data, x2, y, workspace.primaryColorSelector.color, 0x0, 0, " ") + end + + workspace:draw() + end + + workspace.currentToolLayout:addChild(rasterizeLineButton).onTouch = function() + screen.rasterizeLine( + touchX - workspace.image.x + 1, + touchY - workspace.image.y + 1, + dragX - workspace.image.x + 1, + dragY - workspace.image.y + 1, + function(x, y) + image.set(workspace.image.data, x, y, workspace.primaryColorSelector.color, 0x0, 0, " ") + end + ) + + workspace:draw() + end + + workspace.currentToolLayout:addChild(rasterizeEllipseButton).onTouch = function() + local minX, minY, maxX, maxY = math.min(touchX, dragX), math.min(touchY, dragY), math.max(touchX, dragX), math.max(touchY, dragY) + local centerX, centerY = math.ceil(minX + (maxX - minX) / 2), math.ceil(minY + (maxY - minY) / 2) + + screen.rasterizeEllipse( + centerX - workspace.image.x + 1, + centerY - workspace.image.y + 1, + maxX - centerX, + maxY - centerY, + function(x, y) + image.set(workspace.image.data, x, y, workspace.primaryColorSelector.color, 0x0, 0, " ") + end + ) + + workspace:draw() + end + + workspace.currentToolLayout:addChild(clearButton).onTouch = function() + for j = selector.y, selector.y + selector.height - 1 do + for i = selector.x, selector.x + selector.width - 1 do + image.set(workspace.image.data, i - workspace.image.x + 1, j - workspace.image.y + 1, 0x0, 0x0, 1, " ") + end + end + + workspace:draw() + end + + workspace.currentToolLayout:addChild(cropButton).onTouch = function() + workspace.image.data = image.crop(workspace.image.data, selector.x - workspace.image.x + 1, selector.y - workspace.image.y + 1, selector.width, selector.height) + workspace.image.reposition() + fitSelector(workspace) + end + + workspace.currentToolOverlay:addChild(selector) + fitSelector(workspace) +end + +tool.eventHandler = function(workspace, object, e1, e2, e3, e4) + if e1 == "touch" then + touchX, touchY, dragX, dragY = e3, e4, e3, e4 + repositionSelector(workspace) + elseif e1 == "drag" then + dragX, dragY = e3, e4 + repositionSelector(workspace) + end +end + +selector.eventHandler = tool.eventHandler +selector.draw = function() + local step = true + for x = selector.x + 1, selector.x + selector.width - 2 do + screen.drawText(x, selector.y, step and 0xFFFFFF or 0x0, "━") + screen.drawText(x, selector.y + selector.height - 1, step and 0xFFFFFF or 0x0, "━") + step = not step + end + + step = true + for y = selector.y + 1, selector.y + selector.height - 2 do + screen.drawText(selector.x, y, step and 0xFFFFFF or 0x0, "┃") + screen.drawText(selector.x + selector.width - 1, y, step and 0xFFFFFF or 0x0, "┃") + step = not step + end + + screen.drawText(selector.x, selector.y, 0x0, "┏") + screen.drawText(selector.x + selector.width - 1, selector.y + selector.height - 1, 0x0, "┛") + + screen.drawText(selector.x + selector.width - 1, selector.y, 0x0, "┓") + screen.drawText(selector.x, selector.y + selector.height - 1, 0x0, "┗") + + screen.drawText(touchX, touchY, 0x66FF80, "⬤") + screen.drawText(dragX, dragY, 0x66FF80, "⬤") +end + +------------------------------------------------------ + +return tool \ No newline at end of file diff --git a/Applications/PictureEdit/Tools/2.lua b/Applications/Picture Edit.app/Tools/2.lua similarity index 71% rename from Applications/PictureEdit/Tools/2.lua rename to Applications/Picture Edit.app/Tools/2.lua index cfd7c3dd..27b87adb 100644 --- a/Applications/PictureEdit/Tools/2.lua +++ b/Applications/Picture Edit.app/Tools/2.lua @@ -9,15 +9,15 @@ tool.keyCode = 47 tool.about = "Move tool allows you to move image as you wish. But be careful: large images will take a time to shift and redraw. Hello, shitty GPUs!" local xOld, yOld -tool.eventHandler = function(application, object, e1, e2, e3, e4) +tool.eventHandler = function(workspace, object, e1, e2, e3, e4) if e1 == "touch" then xOld, yOld = e3, e4 elseif e1 == "drag" and xOld and yOld then - application.image.localX = application.image.localX + (e3 - xOld) - application.image.localY = application.image.localY + (e4 - yOld) + workspace.image.localX = workspace.image.localX + (e3 - xOld) + workspace.image.localY = workspace.image.localY + (e4 - yOld) xOld, yOld = e3, e4 - application:draw() + workspace:draw() elseif e1 == "drop" then xOld, yOld = nil, nil end diff --git a/Applications/PictureEdit/Tools/3.lua b/Applications/Picture Edit.app/Tools/3.lua similarity index 78% rename from Applications/PictureEdit/Tools/3.lua rename to Applications/Picture Edit.app/Tools/3.lua index af8f5c96..b4cb7e73 100644 --- a/Applications/PictureEdit/Tools/3.lua +++ b/Applications/Picture Edit.app/Tools/3.lua @@ -66,18 +66,18 @@ end set(2, 2) -tool.onSelection = function(application) - application.currentToolLayout:addChild(buttonsLayout) - application.currentToolLayout:addChild(widthInput) - application.currentToolLayout:addChild(heightInput) - application.currentToolLayout:addChild(expandButton) - application.currentToolLayout:addChild(cropButton) +tool.onSelection = function(workspace) + workspace.currentToolLayout:addChild(buttonsLayout) + workspace.currentToolLayout:addChild(widthInput) + workspace.currentToolLayout:addChild(heightInput) + workspace.currentToolLayout:addChild(expandButton) + workspace.currentToolLayout:addChild(cropButton) widthInput.onInputFinished = function() expandButton.disabled = not widthInput.text:match("^%d+$") or not heightInput.text:match("^%d+$") cropButton.disabled = expandButton.disabled - application:draw() + workspace:draw() end heightInput.onInputFinished = widthInput.onInputFinished widthInput.onInputFinished() @@ -85,33 +85,33 @@ tool.onSelection = function(application) expandButton.onTouch = function() local width, height = tonumber(widthInput.text), tonumber(heightInput.text) - application.image.data = image.expand(application.image.data, + workspace.image.data = image.expand(workspace.image.data, currentY > 1 and height or 0, currentY < 3 and height or 0, currentX > 1 and width or 0, currentX < 3 and width or 0, 0x0, 0x0, 1, " ") - application.image.reposition() - application:draw() + workspace.image.reposition() + workspace:draw() end cropButton.onTouch = function() local width, height = tonumber(widthInput.text), tonumber(heightInput.text) - application.image.data = image.crop(application.image.data, + workspace.image.data = image.crop(workspace.image.data, currentX == 1 and 1 or width + 1, currentY == 1 and 1 or height + 1, - (currentX == 1 or currentX == 3) and application.image.width - width or application.image.width - width * 2, - (currentY == 1 or currentY == 3) and application.image.height - height or application.image.height - height * 2 + (currentX == 1 or currentX == 3) and workspace.image.width - width or workspace.image.width - width * 2, + (currentY == 1 or currentY == 3) and workspace.image.height - height or workspace.image.height - height * 2 ) - application.image.reposition() - application:draw() + workspace.image.reposition() + workspace:draw() end end -tool.eventHandler = function(application, object, e1) +tool.eventHandler = function(workspace, object, e1) end diff --git a/Applications/PictureEdit/Tools/4.lua b/Applications/Picture Edit.app/Tools/4.lua similarity index 61% rename from Applications/PictureEdit/Tools/4.lua rename to Applications/Picture Edit.app/Tools/4.lua index be1c1c79..d7dd7a82 100644 --- a/Applications/PictureEdit/Tools/4.lua +++ b/Applications/Picture Edit.app/Tools/4.lua @@ -12,26 +12,26 @@ tool.about = "Picker tool allows to select interested data from image as primary local pickBackgroundSwitch = GUI.switchAndLabel(1, 1, width, 6, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, "Pick background:", true) local pickForegroundSwitch = GUI.switchAndLabel(1, 1, width, 6, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, "Pick foreground:", true) -tool.onSelection = function(application) - application.currentToolLayout:addChild(pickBackgroundSwitch) - application.currentToolLayout:addChild(pickForegroundSwitch) +tool.onSelection = function(workspace) + workspace.currentToolLayout:addChild(pickBackgroundSwitch) + workspace.currentToolLayout:addChild(pickForegroundSwitch) end -tool.eventHandler = function(application, object, e1, e2, e3, e4) +tool.eventHandler = function(workspace, object, e1, e2, e3, e4) if e1 == "touch" or e1 == "drag" then - local x, y = e3 - application.image.x + 1, e4 - application.image.y + 1 + local x, y = e3 - workspace.image.x + 1, e4 - workspace.image.y + 1 - local background, foreground = image.get(application.image.data, x, y) + local background, foreground = image.get(workspace.image.data, x, y) if pickBackgroundSwitch.switch.state then - application.secondaryColorSelector.color = background + workspace.secondaryColorSelector.color = background end if pickForegroundSwitch.switch.state then - application.primaryColorSelector.color = foreground + workspace.primaryColorSelector.color = foreground end - application:draw() + workspace:draw() end end diff --git a/Applications/PictureEdit/Tools/5.lua b/Applications/Picture Edit.app/Tools/5.lua similarity index 64% rename from Applications/PictureEdit/Tools/5.lua rename to Applications/Picture Edit.app/Tools/5.lua index 96d5a385..73c53386 100644 --- a/Applications/PictureEdit/Tools/5.lua +++ b/Applications/Picture Edit.app/Tools/5.lua @@ -1,5 +1,4 @@ -local unicode = require("unicode") local image = require("image") local GUI = require("GUI") local keyboard = require("keyboard") @@ -25,28 +24,28 @@ local radiusSlider = GUI.slider(1, 1, width, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878 radiusSlider.height = 2 radiusSlider.roundValues = true -tool.onSelection = function(application) - application.currentToolLayout:addChild(backgroundSwitch) - application.currentToolLayout:addChild(foregroundSwitch) - application.currentToolLayout:addChild(alphaSwitch) - application.currentToolLayout:addChild(symbolSwitch) - application.currentToolLayout:addChild(symbolInput) - application.currentToolLayout:addChild(alphaSlider) - application.currentToolLayout:addChild(radiusSlider) +tool.onSelection = function(workspace) + workspace.currentToolLayout:addChild(backgroundSwitch) + workspace.currentToolLayout:addChild(foregroundSwitch) + workspace.currentToolLayout:addChild(alphaSwitch) + workspace.currentToolLayout:addChild(symbolSwitch) + workspace.currentToolLayout:addChild(symbolInput) + workspace.currentToolLayout:addChild(alphaSlider) + workspace.currentToolLayout:addChild(radiusSlider) end -tool.eventHandler = function(application, object, e1, e2, e3, e4) +tool.eventHandler = function(workspace, object, e1, e2, e3, e4) if e1 == "touch" or e1 == "drag" then - local x, y = e3 - application.image.x + 1, e4 - application.image.y + 1 + local x, y = e3 - workspace.image.x + 1, e4 - workspace.image.y + 1 local meow = math.floor(radiusSlider.value) for j = y - meow + 1, y + meow - 1 do for i = x - meow + 1, x + meow - 1 do - if i >= 1 and i <= application.image.width and j >= 1 and j <= application.image.height then - local background, foreground, alpha, symbol = image.get(application.image.data, i, j) - image.set(application.image.data, i, j, - backgroundSwitch.switch.state and application.primaryColorSelector.color or background, - foregroundSwitch.switch.state and application.secondaryColorSelector.color or foreground, + if i >= 1 and i <= workspace.image.width and j >= 1 and j <= workspace.image.height then + local background, foreground, alpha, symbol = image.get(workspace.image.data, i, j) + image.set(workspace.image.data, i, j, + backgroundSwitch.switch.state and workspace.primaryColorSelector.color or background, + foregroundSwitch.switch.state and workspace.secondaryColorSelector.color or foreground, alphaSwitch.switch.state and alphaSlider.value / 255 or alpha, symbolSwitch.switch.state and (symbolInput.text == "" and " " or symbolInput.text) or symbol ) @@ -54,7 +53,7 @@ tool.eventHandler = function(application, object, e1, e2, e3, e4) end end - application:draw() + workspace:draw() end end diff --git a/Applications/PictureEdit/Tools/6.lua b/Applications/Picture Edit.app/Tools/6.lua similarity index 67% rename from Applications/PictureEdit/Tools/6.lua rename to Applications/Picture Edit.app/Tools/6.lua index 8d40c76b..920ab22e 100644 --- a/Applications/PictureEdit/Tools/6.lua +++ b/Applications/Picture Edit.app/Tools/6.lua @@ -17,24 +17,24 @@ local radiusSlider = GUI.slider(1, 1, 1, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, radiusSlider.height = 2 radiusSlider.roundValues = true -tool.onSelection = function(application) - application.currentToolLayout:addChild(backgroundSwitch) - application.currentToolLayout:addChild(foregroundSwitch) - application.currentToolLayout:addChild(alphaSwitch) - application.currentToolLayout:addChild(symbolSwitch) - application.currentToolLayout:addChild(radiusSlider) +tool.onSelection = function(workspace) + workspace.currentToolLayout:addChild(backgroundSwitch) + workspace.currentToolLayout:addChild(foregroundSwitch) + workspace.currentToolLayout:addChild(alphaSwitch) + workspace.currentToolLayout:addChild(symbolSwitch) + workspace.currentToolLayout:addChild(radiusSlider) end -tool.eventHandler = function(application, object, e1, e2, e3, e4) +tool.eventHandler = function(workspace, object, e1, e2, e3, e4) if e1 == "touch" or e1 == "drag" then - local x, y = e3 - application.image.x + 1, e4 - application.image.y + 1 + local x, y = e3 - workspace.image.x + 1, e4 - workspace.image.y + 1 local meow = math.floor(radiusSlider.value) for j = y - meow + 1, y + meow - 1 do for i = x - meow + 1, x + meow - 1 do - if i >= 1 and i <= application.image.width and j >= 1 and j <= application.image.height then - local background, foreground, alpha, symbol = image.get(application.image.data, i, j) - image.set(application.image.data, i, j, + if i >= 1 and i <= workspace.image.width and j >= 1 and j <= workspace.image.height then + local background, foreground, alpha, symbol = image.get(workspace.image.data, i, j) + image.set(workspace.image.data, i, j, backgroundSwitch.switch.state and 0x0 or background, foregroundSwitch.switch.state and 0x0 or foreground, alphaSwitch.switch.state and 1 or alpha, @@ -44,7 +44,7 @@ tool.eventHandler = function(application, object, e1, e2, e3, e4) end end - application:draw() + workspace:draw() end end diff --git a/Applications/Picture Edit.app/Tools/7.lua b/Applications/Picture Edit.app/Tools/7.lua new file mode 100644 index 00000000..320398d5 --- /dev/null +++ b/Applications/Picture Edit.app/Tools/7.lua @@ -0,0 +1,51 @@ + +local image = require("image") +local GUI = require("GUI") +local tool = {} + +------------------------------------------------------ + +tool.shortcut = "Tx" +tool.keyCode = 20 +tool.about = "Text tool allows you to type some text data with selected primary color right on your image! It's time to say \"ur mom gay\" to everyone <3" + +tool.eventHandler = function(workspace, object, e1, e2, e3, e4) + if e1 == "touch" then + local input = workspace:addChild(GUI.input( + e3 - 1, + e4, + workspace.image.x + workspace.image.width - e3 + 2, + 1, + nil, + workspace.primaryColorSelector.color, + workspace.primaryColorSelector.color, + nil, + workspace.primaryColorSelector.color, + "" + )) + + input.onInputFinished = function() + if #input.text > 0 then + local x, y = e3 - workspace.image.x + 1, e4 - workspace.image.y + 1 + for i = 1, unicode.len(input.text) do + if x <= workspace.image.width then + local background, foreground, alpha = image.get(workspace.image.data, x, y) + image.set(workspace.image.data, x, y, background, workspace.primaryColorSelector.color, alpha, unicode.sub(input.text, i, i)) + x = x + 1 + else + break + end + end + end + + input:remove() + workspace:draw() + end + + input:startInput() + end +end + +------------------------------------------------------ + +return tool \ No newline at end of file diff --git a/Applications/PictureEdit/Tools/8.lua b/Applications/Picture Edit.app/Tools/8.lua similarity index 81% rename from Applications/PictureEdit/Tools/8.lua rename to Applications/Picture Edit.app/Tools/8.lua index a7443f7a..6aca1985 100644 --- a/Applications/PictureEdit/Tools/8.lua +++ b/Applications/Picture Edit.app/Tools/8.lua @@ -29,13 +29,13 @@ local function pizda(x, y, picture, sourceB, sourceF, sourceA, sourceS, newB, ne if check(x - 1, y, picture, sourceB, sourceF, sourceA, sourceS, newB, newF, newA, newS) then pizda(x - 1, y, picture, sourceB, sourceF, sourceA, sourceS, newB, newF, newA, newS) end end -tool.eventHandler = function(application, object, e1, e2, e3, e4) +tool.eventHandler = function(workspace, object, e1, e2, e3, e4) if e1 == "touch" then - local x, y = e3 - application.image.x + 1, e4 - application.image.y + 1 - local sourceB, sourceF, sourceA, sourceS = image.get(application.image.data, x, y) - pizda(x, y, application.image.data, sourceB, sourceF, sourceA, sourceS, application.primaryColorSelector.color, 0x0, 0, " ") + local x, y = e3 - workspace.image.x + 1, e4 - workspace.image.y + 1 + local sourceB, sourceF, sourceA, sourceS = image.get(workspace.image.data, x, y) + pizda(x, y, workspace.image.data, sourceB, sourceF, sourceA, sourceS, workspace.primaryColorSelector.color, 0x0, 0, " ") - application:draw() + workspace:draw() end end diff --git a/Applications/PictureEdit/Tools/9.lua b/Applications/Picture Edit.app/Tools/9.lua similarity index 65% rename from Applications/PictureEdit/Tools/9.lua rename to Applications/Picture Edit.app/Tools/9.lua index 6a224949..b44de2a5 100644 --- a/Applications/PictureEdit/Tools/9.lua +++ b/Applications/Picture Edit.app/Tools/9.lua @@ -1,6 +1,7 @@ local image = require("image") local GUI = require("GUI") +local text = require("text") local tool = {} ------------------------------------------------------ @@ -21,7 +22,7 @@ for y = 1, 8, 2 do data[i] = container.children[i].pressed and 1 or 0 end - char = string.brailleChar(table.unpack(data)) + char = text.brailleChar(table.unpack(data)) end step = not step @@ -32,24 +33,24 @@ end local backgroundSwitch = GUI.switchAndLabel(1, 1, 1, 6, 0x66DB80, 0x2D2D2D, 0xE1E1E1, 0x878787, "Draw background:", false) -tool.onSelection = function(application) - application.currentToolLayout:addChild(layout) - application.currentToolLayout:addChild(backgroundSwitch) +tool.onSelection = function(workspace) + workspace.currentToolLayout:addChild(layout) + workspace.currentToolLayout:addChild(backgroundSwitch) end -tool.eventHandler = function(application, object, e1, e2, e3, e4) +tool.eventHandler = function(workspace, object, e1, e2, e3, e4) if e1 == "touch" or e1 == "drag" then - local x, y = e3 - application.image.x + 1, e4 - application.image.y + 1 - local background, foreground, alpha, symbol = image.get(application.image.data, x, y) + local x, y = e3 - workspace.image.x + 1, e4 - workspace.image.y + 1 + local background, foreground, alpha, symbol = image.get(workspace.image.data, x, y) - image.set(application.image.data, x, y, - backgroundSwitch.switch.state and application.secondaryColorSelector.color or background, - application.primaryColorSelector.color, + image.set(workspace.image.data, x, y, + backgroundSwitch.switch.state and workspace.secondaryColorSelector.color or background, + workspace.primaryColorSelector.color, backgroundSwitch.switch.state and 0 or alpha, char ) - application:draw() + workspace:draw() end end diff --git a/Applications/PictureEdit/Main.lua b/Applications/PictureEdit/Main.lua deleted file mode 100644 index 4e4c4764..00000000 --- a/Applications/PictureEdit/Main.lua +++ /dev/null @@ -1,493 +0,0 @@ - -local args, options = require("shell").parse(...) -local GUI = require("GUI") -local web = require("web") -local MineOSCore = require("MineOSCore") -local fs = require("filesystem") -local image = require("image") -local unicode = require("unicode") -local color = require("color") -local buffer = require("doubleBuffering") -local MineOSPaths = require("MineOSPaths") -local MineOSInterface = require("MineOSInterface") - --------------------------------------------------------------------- - -local recentColorsLimit = 52 -local recentFilesLimit = 10 - -local config = { - recentColors = {}, - recentFiles = {}, - transparencyBackground = 0xFFFFFF, - transparencyForeground = 0xD2D2D2, -} - -local resourcesPath = MineOSCore.getCurrentScriptDirectory() -local toolsPath = resourcesPath .. "Tools/" -local configPath = MineOSPaths.applicationData .. "Picture Edit/Config2.cfg" -local savePath -local saveItem -local tool - --------------------------------------------------------------------- - -local function saveConfig() - table.toFile(configPath, config) -end - -local function loadConfig() - if fs.exists(configPath) then - config = table.fromFile(configPath) - else - local perLine = 13 - local value, step = 0, 360 / (recentColorsLimit - perLine) - for i = 1, recentColorsLimit - perLine do - table.insert(config.recentColors, color.HSBToInteger(value, 1, 1)) - value = value + step - end - - value, step = 0, 255 / perLine - for i = 1, perLine do - table.insert(config.recentColors, color.RGBToInteger(math.floor(value), math.floor(value), math.floor(value))) - value = value + step - end - - saveConfig() - end -end - -local function addRecentFile(path) - for i = 1, #config.recentFiles do - if config.recentFiles[i] == path then - return - end - end - - table.insert(config.recentFiles, 1, path) - if #config.recentFiles > recentFilesLimit then - table.remove(config.recentFiles, #config.recentFiles) - end - - saveConfig() -end - -loadConfig() - -local application = GUI.application() - -application.menu = application:addChild(GUI.menu(1, 1, application.width, 0xE1E1E1, 0x5A5A5A, 0x3366CC, 0xFFFFFF, nil)) - -local function addTitle(container, text) - local titleContainer = container:addChild(GUI.container(1, 1, container.width, 1)) - titleContainer:addChild(GUI.panel(1, 1, titleContainer.width, 1, 0x2D2D2D)) - titleContainer:addChild(GUI.text(2, 1, 0xD2D2D2, text)) - - return titleContainer -end - -local pizdaWidth = 28 -application.sidebarPanel = application:addChild(GUI.panel(application.width - pizdaWidth + 1, 2, pizdaWidth, application.height - 1, 0x3C3C3C)) -application.sidebarLayout = application:addChild(GUI.layout(application.sidebarPanel.localX, 2, application.sidebarPanel.width, application.sidebarPanel.height, 1, 1)) -application.sidebarLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) - -addTitle(application.sidebarLayout, "Recent colors") - -local recentColorsContainer = application.sidebarLayout:addChild(GUI.container(1, 1, application.sidebarLayout.width - 2, 4)) -local x, y = 1, 1 -for i = 1, #config.recentColors do - local button = recentColorsContainer:addChild(GUI.button(x, y, 2, 1, 0x0, 0x0, 0x0, 0x0, " ")) - button.onTouch = function() - application.primaryColorSelector.color = config.recentColors[i] - application:draw() - end - - x = x + 2 - if x > recentColorsContainer.width - 1 then - x, y = 1, y + 1 - end -end - -local currentToolTitle = addTitle(application.sidebarLayout, "Tool properties") - -application.currentToolLayout = application.sidebarLayout:addChild(GUI.layout(1, 1, application.sidebarLayout.width, 1, 1, 1)) -application.currentToolLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) -application.currentToolLayout:setFitting(1, 1, true, false, 2, 0) - -local aboutToolTitle = addTitle(application.sidebarLayout, "About tool") -local aboutToolTextBox = application.sidebarLayout:addChild(GUI.textBox(1, 1, application.sidebarLayout.width - 2, 1, nil, 0x787878, {}, 1, 0, 0)) - -application.toolsList = application:addChild(GUI.list(1, 2, 6, application.height - 1, 3, 0, 0x3C3C3C, 0xD2D2D2, 0x3C3C3C, 0xD2D2D2, 0x2D2D2D, 0xD2D2D2)) -application.backgroundPanel = application:addChild(GUI.panel(application.toolsList.width + 1, 2, application.width - application.toolsList.width - application.sidebarPanel.width, application.height - 1, 0x1E1E1E)) -application.image = application:addChild(GUI.object(1, 1, 1, 1)) -application.image.data = {} - -local function onToolTouch(index) - tool = application.toolsList:getItem(index).tool - - application.toolsList.selectedItem = index - application.currentToolOverlay:removeChildren() - application.currentToolLayout:removeChildren() - - currentToolTitle.hidden = not tool.onSelection - application.currentToolLayout.hidden = currentToolTitle.hidden - - if tool.onSelection then - local result, reason = pcall(tool.onSelection, application) - if result then - application.currentToolLayout:update() - local lastChild = application.currentToolLayout.children[#application.currentToolLayout.children] - if lastChild then - application.currentToolLayout.height = lastChild.localY + lastChild.height - 1 - end - else - GUI.alert(reason) - end - end - - aboutToolTitle.hidden = not tool.about - aboutToolTextBox.hidden = aboutToolTitle.hidden - - if tool.about then - aboutToolTextBox.lines = string.wrap({tool.about}, aboutToolTextBox.width) - aboutToolTextBox.height = #aboutToolTextBox.lines - end - - application:draw() -end - -local modules = fs.sortedList(toolsPath, "name", false) -for i = 1, #modules do - local result, reason = loadfile(toolsPath .. modules[i]) - if result then - result, reason = pcall(result) - if result then - local item = application.toolsList:addItem(reason.shortcut) - item.tool = reason - item.onTouch = function() - onToolTouch(i) - end - else - error("Failed to perform pcall() on module " .. modules[i] .. ": " .. reason) - end - else - error("Failed to perform loadfile() on module " .. modules[i] .. ": " .. reason) - end -end - -application.image.draw = function(object) - GUI.drawShadow(object.x, object.y, object.width, object.height, nil, true) - - local y, text = object.y + object.height + 1, "Size: " .. object.width .. "x" .. object.height - buffer.drawText(math.floor(object.x + object.width / 2 - unicode.len(text) / 2), y, 0x5A5A5A, text) - - if savePath then - text = "Path: " .. savePath - buffer.drawText(math.floor(object.x + object.width / 2 - unicode.len(text) / 2), y + 1, 0x5A5A5A, text) - end - - local x, y, step, notStep, background, foreground, symbol = object.x, object.y, false, application.image.width % 2 - for i = 1, application.image.width * application.image.height do - if application.image.data[5][i] == 0 then - background = application.image.data[3][i] - foreground = application.image.data[4][i] - symbol = application.image.data[6][i] - elseif application.image.data[5][i] < 1 then - background = color.blend(config.transparencyBackground, application.image.data[3][i], application.image.data[5][i]) - foreground = application.image.data[4][i] - symbol = application.image.data[6][i] - else - if application.image.data[6][i] == " " then - background = config.transparencyBackground - foreground = config.transparencyForeground - symbol = step and "▒" or "░" - else - background = config.transparencyBackground - foreground = application.image.data[4][i] - symbol = application.image.data[6][i] - end - end - - buffer.set(x, y, background, foreground, symbol) - - x, step = x + 1, not step - if x > object.x + object.width - 1 then - x, y = object.x, y + 1 - if notStep == 0 then - step = not step - end - end - end -end - -local function updateRecentColorsButtons() - for i = 1, #config.recentColors do - recentColorsContainer.children[i].colors.default.background = config.recentColors[i] - recentColorsContainer.children[i].colors.pressed.background = 0xFFFFFF - config.recentColors[i] - end -end - -local function swapColors() - application.primaryColorSelector.color, application.secondaryColorSelector.color = application.secondaryColorSelector.color, application.primaryColorSelector.color - application:draw() -end - -local function colorSelectorDraw(object) - buffer.drawRectangle(object.x + 1, object.y, object.width - 2, object.height, object.color, 0x0, " ") - for y = object.y, object.y + object.height - 1 do - buffer.drawText(object.x, y, object.color, "⢸") - buffer.drawText(object.x + object.width - 1, y, object.color, "⡇") - end -end - -application.secondaryColorSelector = application:addChild(GUI.colorSelector(2, application.toolsList.height - 3, 5, 2, 0xFFFFFF, " ")) -application.primaryColorSelector = application:addChild(GUI.colorSelector(1, application.toolsList.height - 4, 5, 2, 0x880000, " ")) -application.secondaryColorSelector.draw, application.primaryColorSelector.draw = colorSelectorDraw, colorSelectorDraw - -application:addChild(GUI.adaptiveButton(3, application.secondaryColorSelector.localY + application.secondaryColorSelector.height + 1, 0, 0, nil, 0xD2D2D2, nil, 0xA5A5A5, "<>")).onTouch = swapColors - -application.image.eventHandler = function(application, object, e1, e2, e3, e4, ...) - if e1 == "key_down" then - -- D - if e4 == 32 then - application.primaryColorSelector.color, application.secondaryColorSelector.color = 0x0, 0xFFFFFF - application:draw() - -- X - elseif e4 == 45 then - swapColors() - else - for i = 1, application.toolsList:count() do - if e4 == application.toolsList:getItem(i).tool.keyCode then - onToolTouch(i) - return - end - end - end - end - - local result, reason = pcall(tool.eventHandler, application, object, e1, e2, e3, e4, ...) - if not result then - GUI.alert("Tool eventHandler() failed: " .. reason) - end -end - -application.image.reposition = function() - application.image.width, application.image.height = application.image.data[1], application.image.data[2] - if application.image.width <= application.backgroundPanel.width then - application.image.localX = math.floor(application.backgroundPanel.x + application.backgroundPanel.width / 2 - application.image.width / 2) - application.image.localY = math.floor(application.backgroundPanel.y + application.backgroundPanel.height / 2 - application.image.height / 2) - else - application.image.localX, application.image.localY = 9, 3 - end -end - -local function newNoGUI(width, height) - savePath, saveItem.disabled = nil, true - application.image.data = {width, height, {}, {}, {}, {}} - application.image.reposition() - - for i = 1, width * height do - table.insert(application.image.data[3], 0x0) - table.insert(application.image.data[4], 0x0) - table.insert(application.image.data[5], 1) - table.insert(application.image.data[6], " ") - end -end - -local function new() - local container = MineOSInterface.addBackgroundContainer(application, "New picture") - - local widthInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x696969, 0xE1E1E1, 0x2D2D2D, "51", "Width")) - local heightInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x696969, 0xE1E1E1, 0x2D2D2D, "19", "Height")) - - widthInput.validator = function(text) - return tonumber(text) - end - heightInput.validator = widthInput.validator - - container.panel.eventHandler = function(application, object, e1) - if e1 == "touch" then - newNoGUI(tonumber(widthInput.text), tonumber(heightInput.text)) - container:remove() - application:draw() - end - end - - application:draw() -end - -local function loadImage(path) - local result, reason = image.load(path) - if result then - savePath, saveItem.disabled = path, false - addRecentFile(path) - application.image.data = result - application.image.reposition() - else - GUI.alert(reason) - end -end - -local function saveImage(path) - if fs.extension(path) == ".pic" then - local result, reason = image.save(path, application.image.data, 6) - if result then - savePath, saveItem.disabled = path, false - - addRecentFile(path) - else - GUI.alert(reason) - end - else - savePath, saveItem.disabled = path, false - - local file = io.open(path, "wb") - file:write(image.toString(application.image.data)) - file:close() - end -end - -application.menu:addItem("PE", 0x00B6FF) - -local fileItem = application.menu:addContextMenu("File") -fileItem:addItem("New").onTouch = new - -fileItem:addSeparator() - -fileItem:addItem("Open").onTouch = function() - local filesystemDialog = GUI.addFilesystemDialog(application, true, 50, math.floor(application.height * 0.8), "Open", "Cancel", "File name", "/") - filesystemDialog:setMode(GUI.IO_MODE_OPEN, GUI.IO_MODE_FILE) - filesystemDialog:addExtensionFilter(".pic") - filesystemDialog:expandPath(MineOSPaths.desktop) - filesystemDialog:show() - - filesystemDialog.onSubmit = function(path) - loadImage(path) - application:draw() - end -end - -local fileItemSubMenu = fileItem:addSubMenu("Open recent", #config.recentFiles == 0) -for i = 1, #config.recentFiles do - fileItemSubMenu:addItem(string.limit(config.recentFiles[i], 32, "left")).onTouch = function() - loadImage(config.recentFiles[i]) - application:draw() - end -end - -fileItem:addItem("Open from URL").onTouch = function() - local container = MineOSInterface.addBackgroundContainer(application, "Open from URL") - - local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x969696, 0xE1E1E1, 0x2D2D2D, "", "http://example.com/test.pic")) - input.onInputFinished = function() - if #input.text > 0 then - input:remove() - container.layout:addChild(GUI.label(1, 1, container.width, 1, 0x969696, "Downloading file..."):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)) - application:draw() - - local temporaryPath = MineOSCore.getTemporaryPath() .. ".pic" - local result, reason = web.download(input.text, temporaryPath) - - container:remove() - - if result then - loadImage(temporaryPath) - fs.remove(temporaryPath) - savePath, saveItem.disabled = nil, true - else - GUI.alert(reason) - end - - application:draw() - end - end - - application:draw() -end - -fileItem:addSeparator() - -saveItem = fileItem:addItem("Save") -saveItem.onTouch = function() - saveImage(savePath) -end - -fileItem:addItem("Save as").onTouch = function() - local filesystemDialog = GUI.addFilesystemDialog(application, true, 50, math.floor(application.height * 0.8), "Save", "Cancel", "File name", "/") - filesystemDialog:setMode(GUI.IO_MODE_SAVE, GUI.IO_MODE_FILE) - filesystemDialog:addExtensionFilter(".pic") - filesystemDialog:addExtensionFilter(".ocifstring") - filesystemDialog:expandPath(MineOSPaths.desktop) - filesystemDialog.filesystemTree.selectedItem = MineOSPaths.desktop - filesystemDialog:show() - - filesystemDialog.onSubmit = function(path) - saveImage(path) - end -end - -fileItem:addSeparator() - -fileItem:addItem("Exit").onTouch = function() - application:stop() -end - -application.menu:addItem("View").onTouch = function() - local container = MineOSInterface.addBackgroundContainer(application, "View") - - local colorSelector1 = container.layout:addChild(GUI.colorSelector(1, 1, 36, 3, config.transparencyBackground, "Transparency background")) - local colorSelector2 = container.layout:addChild(GUI.colorSelector(1, 1, 36, 3, config.transparencyForeground, "Transparency foreground")) - - container.panel.eventHandler = function(application, object, e1) - if e1 == "touch" then - config.transparencyBackground, config.transparencyForeground = colorSelector1.color, colorSelector2.color - - container:remove() - application:draw() - saveConfig() - end - end - - application:draw() -end - -application.menu:addItem("Hotkeys").onTouch = function() - local container = MineOSInterface.addBackgroundContainer(application, "Hotkeys") - local lines = { - "There are some hotkeys that works exactly like in real Photoshop:", - " ", - "M - selection tool", - "V - move tool", - "C - resizer tool", - "Alt - picker tool", - "B - brush tool", - "E - eraser tool", - "T - text tool", - "G - fill tool", - "F - braille tool", - " ", - "X - switch colors", - "D - make colors B/W", - } - - container.layout:addChild(GUI.textBox(1, 1, 36, 1, nil, 0x969696, lines, 1, 0, 0, true, true)).eventHandler = nil - application:draw() -end - -application.currentToolOverlay = application:addChild(GUI.container(1, 1, application.width, application.height)) - ----------------------------------------------------------------- - -application.image:moveToBack() -application.backgroundPanel:moveToBack() - -updateRecentColorsButtons() - -if options.o or options.open and args[1] and fs.exists(args[1]) then - loadImage(args[1]) -else - newNoGUI(51, 19) -end - -onToolTouch(5) -application:start() \ No newline at end of file diff --git a/Applications/PictureEdit/Tools/1.lua b/Applications/PictureEdit/Tools/1.lua deleted file mode 100644 index b8dbc8b8..00000000 --- a/Applications/PictureEdit/Tools/1.lua +++ /dev/null @@ -1,160 +0,0 @@ - -local GUI = require("GUI") -local buffer = require("doubleBuffering") -local image = require("image") -local tool = {} - ------------------------------------------------------- - -tool.shortcut = "Se" -tool.keyCode = 50 -tool.about = "Selection tool allows you to select preferred area on image and to perform some operations on it. Green dots mean start and end points (for example, it needs to line rasterization)" - -local selector, touchX, touchY, dragX, dragY = GUI.object(1, 1, 1, 1) - -local fillButton = GUI.roundedButton(1, 1, 36, 1, 0xE1E1E1, 0x2D2D2D, 0x2D2D2D, 0xE1E1E1, "Fill") -local outlineButton = GUI.roundedButton(1, 1, 36, 1, 0xE1E1E1, 0x2D2D2D, 0x2D2D2D, 0xE1E1E1, "Outline") -local rasterizeLineButton = GUI.roundedButton(1, 1, 36, 1, 0xE1E1E1, 0x2D2D2D, 0x2D2D2D, 0xE1E1E1, "Rasterize line") -local rasterizeEllipseButton = GUI.roundedButton(1, 1, 36, 1, 0xE1E1E1, 0x2D2D2D, 0x2D2D2D, 0xE1E1E1, "Rasterize ellipse") -local clearButton = GUI.roundedButton(1, 1, 36, 1, 0x696969, 0xE1E1E1, 0x2D2D2D, 0xE1E1E1, "Clear") -local cropButton = GUI.roundedButton(1, 1, 36, 1, 0x696969, 0xE1E1E1, 0x2D2D2D, 0xE1E1E1, "Crop") - -local function repositionSelector(application) - if dragX - touchX >= 0 then - selector.localX, selector.width = touchX, dragX - touchX + 1 - else - selector.localX, selector.width = dragX, touchX - dragX + 1 - end - - if dragY - touchY >= 0 then - selector.localY, selector.height = touchY, dragY - touchY + 1 - else - selector.localY, selector.height = dragY, touchY - dragY + 1 - end - - application:draw() -end - -local function fitSelector(application) - touchX, touchY, dragX, dragY = application.image.localX, application.image.localY, application.image.localX + application.image.width - 1, application.image.localY + application.image.height - 1 - repositionSelector(application) -end - -tool.onSelection = function(application) - application.currentToolLayout:addChild(fillButton).onTouch = function() - for j = selector.y, selector.y + selector.height - 1 do - for i = selector.x, selector.x + selector.width - 1 do - image.set(application.image.data, i - application.image.x + 1, j - application.image.y + 1, application.primaryColorSelector.color, 0x0, 0, " ") - end - end - - application:draw() - end - - application.currentToolLayout:addChild(outlineButton).onTouch = function() - local x1, y1 = selector.x - application.image.x + 1, selector.y - application.image.y + 1 - local x2, y2 = x1 + selector.width - 1, y1 + selector.height - 1 - - for x = x1, x2 do - image.set(application.image.data, x, y1, application.primaryColorSelector.color, 0x0, 0, " ") - image.set(application.image.data, x, y2, application.primaryColorSelector.color, 0x0, 0, " ") - end - - for y = y1 + 1, y2 - 1 do - image.set(application.image.data, x1, y, application.primaryColorSelector.color, 0x0, 0, " ") - image.set(application.image.data, x2, y, application.primaryColorSelector.color, 0x0, 0, " ") - end - - application:draw() - end - - application.currentToolLayout:addChild(rasterizeLineButton).onTouch = function() - buffer.rasterizeLine( - touchX - application.image.x + 1, - touchY - application.image.y + 1, - dragX - application.image.x + 1, - dragY - application.image.y + 1, - function(x, y) - image.set(application.image.data, x, y, application.primaryColorSelector.color, 0x0, 0, " ") - end - ) - - application:draw() - end - - application.currentToolLayout:addChild(rasterizeEllipseButton).onTouch = function() - local minX, minY, maxX, maxY = math.min(touchX, dragX), math.min(touchY, dragY), math.max(touchX, dragX), math.max(touchY, dragY) - local centerX, centerY = math.ceil(minX + (maxX - minX) / 2), math.ceil(minY + (maxY - minY) / 2) - - buffer.rasterizeEllipse( - centerX - application.image.x + 1, - centerY - application.image.y + 1, - maxX - centerX, - maxY - centerY, - function(x, y) - image.set(application.image.data, x, y, application.primaryColorSelector.color, 0x0, 0, " ") - end - ) - - application:draw() - end - - application.currentToolLayout:addChild(clearButton).onTouch = function() - for j = selector.y, selector.y + selector.height - 1 do - for i = selector.x, selector.x + selector.width - 1 do - image.set(application.image.data, i - application.image.x + 1, j - application.image.y + 1, 0x0, 0x0, 1, " ") - end - end - - application:draw() - end - - application.currentToolLayout:addChild(cropButton).onTouch = function() - application.image.data = image.crop(application.image.data, selector.x - application.image.x + 1, selector.y - application.image.y + 1, selector.width, selector.height) - application.image.reposition() - fitSelector(application) - end - - application.currentToolOverlay:addChild(selector) - fitSelector(application) -end - -tool.eventHandler = function(application, object, e1, e2, e3, e4) - if e1 == "touch" then - touchX, touchY, dragX, dragY = e3, e4, e3, e4 - repositionSelector(application) - elseif e1 == "drag" then - dragX, dragY = e3, e4 - repositionSelector(application) - end -end - -selector.eventHandler = tool.eventHandler -selector.draw = function() - local step = true - for x = selector.x + 1, selector.x + selector.width - 2 do - buffer.drawText(x, selector.y, step and 0xFFFFFF or 0x0, "━") - buffer.drawText(x, selector.y + selector.height - 1, step and 0xFFFFFF or 0x0, "━") - step = not step - end - - step = true - for y = selector.y + 1, selector.y + selector.height - 2 do - buffer.drawText(selector.x, y, step and 0xFFFFFF or 0x0, "┃") - buffer.drawText(selector.x + selector.width - 1, y, step and 0xFFFFFF or 0x0, "┃") - step = not step - end - - buffer.drawText(selector.x, selector.y, 0x0, "┏") - buffer.drawText(selector.x + selector.width - 1, selector.y + selector.height - 1, 0x0, "┛") - - buffer.drawText(selector.x + selector.width - 1, selector.y, 0x0, "┓") - buffer.drawText(selector.x, selector.y + selector.height - 1, 0x0, "┗") - - buffer.drawText(touchX, touchY, 0x66FF80, "⬤") - buffer.drawText(dragX, dragY, 0x66FF80, "⬤") -end - ------------------------------------------------------- - -return tool \ No newline at end of file diff --git a/Applications/PictureEdit/Tools/7.lua b/Applications/PictureEdit/Tools/7.lua deleted file mode 100644 index 95155f98..00000000 --- a/Applications/PictureEdit/Tools/7.lua +++ /dev/null @@ -1,52 +0,0 @@ - -local unicode = require("unicode") -local image = require("image") -local GUI = require("GUI") -local tool = {} - ------------------------------------------------------- - -tool.shortcut = "Tx" -tool.keyCode = 20 -tool.about = "Text tool allows you to type some text data with selected primary color right on your image! It's time to say \"ur mom gay\" to everyone <3" - -tool.eventHandler = function(application, object, e1, e2, e3, e4) - if e1 == "touch" then - local input = application:addChild(GUI.input( - e3 - 1, - e4, - application.image.x + application.image.width - e3 + 2, - 1, - nil, - application.primaryColorSelector.color, - application.primaryColorSelector.color, - nil, - application.primaryColorSelector.color, - "" - )) - - input.onInputFinished = function() - if #input.text > 0 then - local x, y = e3 - application.image.x + 1, e4 - application.image.y + 1 - for i = 1, unicode.len(input.text) do - if x <= application.image.width then - local background, foreground, alpha = image.get(application.image.data, x, y) - image.set(application.image.data, x, y, background, application.primaryColorSelector.color, alpha, unicode.sub(input.text, i, i)) - x = x + 1 - else - break - end - end - end - - input:remove() - application:draw() - end - - input:startInput() - end -end - ------------------------------------------------------- - -return tool \ No newline at end of file diff --git a/Applications/PrintImage/Icon.pic b/Applications/Print Image.app/Icon.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/PrintImage/Icon.pic rename to Applications/Print Image.app/Icon.pic diff --git a/Applications/PrintImage/Main.lua b/Applications/Print Image.app/Main.lua similarity index 54% rename from Applications/PrintImage/Main.lua rename to Applications/Print Image.app/Main.lua index 4a50c645..26978fca 100755 --- a/Applications/PrintImage/Main.lua +++ b/Applications/Print Image.app/Main.lua @@ -1,316 +1,317 @@ - ------------------------------------------ Libraries ----------------------------------------- - -local component = require("component") -local computer = require("computer") -local unicode = require("unicode") -local fs = require("filesystem") -local advancedLua = require("advancedLua") -local color = require("color") -local image = require("image") -local buffer = require("doubleBuffering") -local GUI = require("GUI") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") - ------------------------------------------ cyka ----------------------------------------- - -if not component.isAvailable("printer3d") then GUI.alert("This program requires at least one 3D-printer"); return end -local args, options = require("shell").parse(...) -local startImagePath = args[1] == "open" and args[2] or "/MineOS/System/Icons/Steve.pic" -local configPath = MineOSPaths.applicationData .. "PrintImage/Config.cfg" -local panelWidth = 34 -local application -local mainImage -local printers -local currentPrinter = 1 -local shapeResolutionLimit = 4 -local timeDelay = 0.05 - ------------------------------------------ Config ----------------------------------------- - -local function save() - table.toFile(configPath, config) -end - -local function load() - if fs.exists(configPath) then - config = table.fromFile(configPath) - else - config = { - mainMaterial = "quartz_block_side", - printName = "My picture", - showGrid = true, - floorMode = false, - frame = {enabled = true, width = 3, material = "planks_spruce"}, - lightEmission = {enabled = false, level = 8} - } - save() - end -end - ------------------------------------------ Printer-related cyka ----------------------------------------- - -local function getPrinters() - printers = {} - for address in pairs(component.list("printer3d")) do table.insert(printers, component.proxy(address)) end -end - -local function addShapePixel(x, y, color, xPrinterPixel, yPrinterPixel) - local pixelSize = math.floor(16 / shapeResolutionLimit) - local xPrinter = x * pixelSize - pixelSize - local yPrinter = y * pixelSize - pixelSize - - if config.floorMode then - printers[currentPrinter].addShape(xPrinter, 0, yPrinter, xPrinter + pixelSize, 16, yPrinter + pixelSize, config.mainMaterial, false, color) - else - if config.frame.enabled then - local xModifyer1, xModifyer2, yModifyer1, yModifyer2 = 0, 0, 0, 0 - if xPrinterPixel == 1 then xModifyer1 = config.frame.width end - if xPrinterPixel == image.getWidth(mainImage) then xModifyer2 = -config.frame.width end - if yPrinterPixel == 1 then yModifyer2 = -config.frame.width end - if yPrinterPixel == image.getHeight(mainImage) * 2 then yModifyer1 = config.frame.width end - printers[currentPrinter].addShape(xPrinter + xModifyer1, yPrinter + yModifyer1, 15, xPrinter + pixelSize + xModifyer2, yPrinter + pixelSize + yModifyer2, 16, config.mainMaterial, false, color) - else - printers[currentPrinter].addShape(xPrinter, 15, yPrinter, xPrinter + pixelSize, 16, yPrinter + pixelSize, config.mainMaterial, false, color) - end - end -end - -local function beginPrint() - buffer.clear(0x0000000, 50) - - local xShape, yShape = 1, 1 - local xShapeCount, yShapeCount = math.ceil(image.getWidth(mainImage) / shapeResolutionLimit), math.ceil(image.getHeight(mainImage) * 2 / shapeResolutionLimit) - local counter = 0 - while true do - if printers[currentPrinter].status() == "idle" then - printers[currentPrinter].reset() - printers[currentPrinter].setLabel(config.printName) - printers[currentPrinter].setTooltip("Part " .. xShape .. "x" .. yShape .. " of " .. xShapeCount .. "x" .. yShapeCount) - if config.lightEmission.enabled then printers[currentPrinter].setLightLevel(config.lightEmission.level) end - - local jReplacer = shapeResolutionLimit - for j = 1, shapeResolutionLimit / 2 do - for i = 1, shapeResolutionLimit do - local xImage = xShape * shapeResolutionLimit - shapeResolutionLimit + i - local yImage = yShape * (shapeResolutionLimit / 2) - (shapeResolutionLimit / 2) + j - - if xImage <= image.getWidth(mainImage) and yImage <= image.getHeight(mainImage) then - local background, foreground, alpha, symbol = image.get(mainImage, xImage, yImage) - if alpha < 0xFF then - if symbol == " " then foreground = background end - addShapePixel(i, jReplacer, background, xImage, yImage * 2 - 1) - addShapePixel(i, jReplacer - 1, foreground, xImage, yImage * 2) - end - - GUI.progressBar(math.floor(buffer.getWidth() / 2 - 25), math.floor(buffer.getHeight() / 2), 50, 0x3366CC, 0xFFFFFF, 0xFFFFFF, math.ceil(counter * 100 / (xShapeCount * yShapeCount)), true, true, "Progress: ", "%"):draw() - buffer.drawChanges() - -- else - -- error("Printing out of mainImage range") - end - end - - jReplacer = jReplacer - 2 - end - - if config.frame.enabled and not config.floorMode then - local xFrame, yFrame = shapeResolutionLimit * (image.getWidth(mainImage) % shapeResolutionLimit), shapeResolutionLimit * ((image.getHeight(mainImage) * 2) % shapeResolutionLimit) - xFrame = xShape == xShapeCount and (xFrame == 0 and 16 or xFrame) or 16 - yFrame = yShape == yShapeCount and (yFrame == 0 and 0 or yFrame) or 0 - - if xShape == 1 then printers[currentPrinter].addShape(0, yFrame, 14, config.frame.width, 16, 16, config.frame.material) end - if xShape == xShapeCount then printers[currentPrinter].addShape(xFrame - config.frame.width, yFrame, 14, xFrame, 16, 16, config.frame.material) end - - if yShape == 1 then printers[currentPrinter].addShape(0, 16 - config.frame.width, 14, xFrame, 16, 16, config.frame.material) end - if yShape == yShapeCount then printers[currentPrinter].addShape(0, yFrame, 14, xFrame, yFrame + config.frame.width, 16, config.frame.material) end - end - - printers[currentPrinter].commit() - - counter = counter + 1 - xShape = xShape + 1 - if xShape > xShapeCount then xShape = 1; yShape = yShape + 1 end - if yShape > yShapeCount then break end - end - - currentPrinter = currentPrinter + 1 - if currentPrinter > #printers then currentPrinter = 1 end - os.sleep(timeDelay) - end - - buffer.clear() - application:draw() -end - ------------------------------------------ Window-zaluped parasha ----------------------------------------- - -local function getStatus() - local xBlocks, yBlocks = math.ceil(image.getWidth(mainImage) / shapeResolutionLimit), math.ceil(image.getHeight(mainImage) * 2 / shapeResolutionLimit) - application.shadeContainer.statusTextBox.lines = { - "Image size: " .. image.getWidth(mainImage) .. "x" .. image.getHeight(mainImage) .. " px", - "Count of printers: " .. #printers, - "Print result: " .. xBlocks .. "x" .. yBlocks .. " blocks", - "Total count: " .. xBlocks * yBlocks .. " blocks" - } -end - -local function verticalLine(x, y, height, transparency) - for i = y, y + height - 1 do - local background = buffer.get(x, i) - buffer.set(x, i, background, color.blend(background, 0xFFFFFF, transparency), "│") - end -end - -local function horizontalLine(x, y, width, transparency) - for i = x, x + width - 1 do - local background, foreground, symbol = buffer.get(i, y) - buffer.set(i, y, background, color.blend(background, 0xFFFFFF, transparency), symbol == "│" and "┼" or "─") - end -end - -local function drawMainImageObject(object) - if mainImage then - local xImage = image.getWidth(mainImage) < buffer.getWidth() and math.floor(buffer.getWidth() / 2 - image.getWidth(mainImage) / 2) or 1 - local yImage = image.getHeight(mainImage) < buffer.getHeight() and math.floor(buffer.getHeight() / 2 - image.getHeight(mainImage) / 2) or 1 - buffer.drawImage(xImage, yImage, mainImage) - GUI.drawShadow(xImage, yImage, image.getWidth(mainImage), image.getHeight(mainImage), 50, true) - if config.showGrid then - for x = xImage, xImage + image.getWidth(mainImage) - 1, shapeResolutionLimit do verticalLine(x, yImage, image.getHeight(mainImage), 0.627) end - for y = yImage, yImage + image.getHeight(mainImage) - 1, shapeResolutionLimit / 2 do horizontalLine(xImage, y, image.getWidth(mainImage), 0.627) end - buffer.drawText(1, 1, 0xBBBBBB, "хуй") - end - end -end - -local function createWindow() - application = GUI.application() - application:addChild(GUI.panel(1, 1, application.width, application.height, 0xEEEEEE)) - application:addChild(GUI.object(1, 1, application.width, application.height)).draw = drawMainImageObject - local textBoxesWidth = math.floor(panelWidth * 0.55) - - application.shadeContainer = application:addChild(GUI.container(application.width - panelWidth + 1, 1, panelWidth, application.height)) - application.shadeContainer:addChild(GUI.panel(1, 1, application.shadeContainer.width, application.shadeContainer.height, 0x0000000, 0.4)) - - local y = 2 - application.shadeContainer:addChild(GUI.label(1, y, application.shadeContainer.width, 1, 0xFFFFFF, "Main properties")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) - - y = y + 2 - application.shadeContainer:addChild(GUI.label(3, y, application.shadeContainer.width, 1, 0xCCCCCC, "Image path:")) - local filesystemChooser = application.shadeContainer:addChild(GUI.filesystemChooser(application.shadeContainer.width - textBoxesWidth - 1, y, textBoxesWidth, 1, 0xEEEEEE, 0x262626, 0x444444, 0x999999, startImagePath, MineOSCore.localization.open, MineOSCore.localization.cancel, "Image path", "/")) - filesystemChooser:addExtensionFilter(".pic") - filesystemChooser.onSubmit = function(path) - mainImage = image.load(path) - getStatus() - application:draw() - end - - y = y + 2 - application.shadeContainer:addChild(GUI.label(3, y, application.shadeContainer.width, 1, 0xCCCCCC, "Material:")) - local mainMaterialTextBox = application.shadeContainer:addChild(GUI.input(application.shadeContainer.width - textBoxesWidth - 1, y, textBoxesWidth, 1, 0xEEEEEE, 0x555555, 0x555555, 0xEEEEEE, 0x262626, config.mainMaterial, nil, false)) - mainMaterialTextBox.onInputFinished = function() - config.mainMaterial = mainMaterialTextBox.text - save() - end - - y = y + 2 - application.shadeContainer:addChild(GUI.label(3, y, application.shadeContainer.width, 1, 0xCCCCCC, "Print name:")) - local printNameTextBox = application.shadeContainer:addChild(GUI.input(application.shadeContainer.width - textBoxesWidth - 1, y, textBoxesWidth, 1, 0xEEEEEE, 0x555555, 0x555555, 0xEEEEEE, 0x262626, config.printName, nil, false)) - printNameTextBox.onInputFinished = function() - config.printName = printNameTextBox.text - save() - end - - y = y + 2 - application.shadeContainer:addChild(GUI.label(3, y, application.shadeContainer.width, 1, 0xCCCCCC, "Floor mode:")) - local floorSwitch = application.shadeContainer:addChild(GUI.switch(application.shadeContainer.width - 9, y, 8, 0xFFDB40, 0xAAAAAA, 0xFFFFFF, config.floorMode)) - floorSwitch.onStateChanged = function() - config.floorMode = floorSwitch.state - save() - end - - y = y + 2 - application.shadeContainer:addChild(GUI.label(3, y, application.shadeContainer.width, 1, 0xCCCCCC, "Show grid:")) - local gridSwitch = application.shadeContainer:addChild(GUI.switch(application.shadeContainer.width - 9, y, 8, 0xFFDB40, 0xAAAAAA, 0xFFFFFF, config.showGrid)) - gridSwitch.onStateChanged = function() - config.showGrid = gridSwitch.state - save() - application:draw() - end - - y = y + 4 - application.shadeContainer:addChild(GUI.label(1, y, application.shadeContainer.width, 1, 0xFFFFFF, "Frame properties")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) - y = y + 2 - application.shadeContainer:addChild(GUI.label(3, y, application.shadeContainer.width, 1, 0xCCCCCC, "Enabled:")) - local frameSwitch = application.shadeContainer:addChild(GUI.switch(application.shadeContainer.width - 9, y, 8, 0xFFDB40, 0xAAAAAA, 0xFFFFFF, config.frame.enabled)) - frameSwitch.onStateChanged = function() - config.frame.enabled = frameSwitch.state - save() - end - y = y + 2 - application.shadeContainer:addChild(GUI.label(3, y, application.shadeContainer.width, 1, 0xCCCCCC, "Material:")) - local frameMaterialTextBox = application.shadeContainer:addChild(GUI.input(application.shadeContainer.width - textBoxesWidth - 1, y, textBoxesWidth, 1, 0xEEEEEE, 0x555555, 0x555555, 0xEEEEEE, 0x262626, config.frame.material, nil, false)) - frameMaterialTextBox.onInputFinished = function() - config.frame.material = frameMaterialTextBox.text - save() - end - - y = y + 2 - local frameWidthSlider = application.shadeContainer:addChild(GUI.slider(3, y, application.shadeContainer.width - 4, 0xFFDB80, 0x000000, 0xFFDB40, 0xCCCCCC, 1, shapeResolutionLimit - 1, config.frame.width, false, "Width: " , " voxel(s)")) - frameWidthSlider.onValueChanged = function() - config.frame.width = frameWidthSlider.value - save() - end - frameWidthSlider.roundValues = true - - y = y + 5 - application.shadeContainer:addChild(GUI.label(1, y, application.shadeContainer.width, 1, 0xFFFFFF, "Light emission")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) - y = y + 2 - application.shadeContainer:addChild(GUI.label(3, y, application.shadeContainer.width, 1, 0xCCCCCC, "Enabled:")) - local lightSwitch = application.shadeContainer:addChild(GUI.switch(application.shadeContainer.width - 9, y, 8, 0xFFDB40, 0xAAAAAA, 0xFFFFFF, config.lightEmission.enabled)) - lightSwitch.onStateChanged = function() - config.lightEmission.enabled = true - save() - end - - y = y + 2 - local lightSlider = application.shadeContainer:addChild(GUI.slider(3, y, application.shadeContainer.width - 4, 0xFFDB80, 0x000000, 0xFFDB40, 0xCCCCCC, 1, 8, 8, false, "Radius: " , " block(s)")) - lightSlider.roundValues = true - lightSlider.onValueChanged = function() - config.lightEmission.value = lightSlider.value - save() - end - - y = y + 5 - application.shadeContainer:addChild(GUI.label(1, y, application.shadeContainer.width, 1, 0xFFFFFF, "Summary information:")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) - y = y + 2 - application.shadeContainer.statusTextBox = application.shadeContainer:addChild(GUI.textBox(3, y, application.shadeContainer.width - 4, 5, nil, 0xCCCCCC, {}, 1)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP) - - application.shadeContainer:addChild(GUI.button(1, application.shadeContainer.height - 5, application.shadeContainer.width, 3, 0x363636, 0xFFFFFF, 0xFFFFFF, 0x262626, "Exit")).onTouch = function() - application:stop() - end - - application.shadeContainer:addChild(GUI.button(1, application.shadeContainer.height - 2, application.shadeContainer.width, 3, 0x262626, 0xFFFFFF, 0xFFFFFF, 0x262626, "Start print")).onTouch = function() - beginPrint() - end - - application.eventHandler = function(application, object, e1, e2, e3) - if (e1 == "component_added" or e1 == "component_removed") and e3 == "printer3d" then - getPrinters() - getStatus() - application:draw() - end - end -end - ------------------------------------------ Shitty meatball rolls ----------------------------------------- - -buffer.flush() -load() -getPrinters() -createWindow() -mainImage = image.load(startImagePath) -getStatus() - -application:draw() -application:start() + +----------------------------------------- Libraries ----------------------------------------- + +local filesystem = require("Filesystem") +local color = require("Color") +local image = require("Image") +local screen = require("Screen") +local GUI = require("GUI") +local paths = require("Paths") +local system = require("System") + +----------------------------------------- cyka ----------------------------------------- + +if not component.isAvailable("printer3d") then + GUI.alert("This program requires at least one 3D-printer") + return +end + +local args, options = system.parseArguments(...) + +local startImagePath = args[1] == "open" and args[2] or paths.system.icons .. "Folder.pic" +local configPath = paths.user.applicationData .. "PrintImage/Config.cfg" +local panelWidth = 34 +local application +local mainImage +local printers +local currentPrinter = 1 +local shapeResolutionLimit = 4 +local timeDelay = 0.05 + +----------------------------------------- Config ----------------------------------------- + +local function save() + filesystem.writeTable(configPath, config) +end + +local function load() + if filesystem.exists(configPath) then + config = filesystem.readTable(configPath) + else + config = { + mainMaterial = "quartz_block_side", + printName = "My picture", + showGrid = true, + floorMode = false, + frame = {enabled = true, width = 3, material = "planks_spruce"}, + lightEmission = {enabled = false, level = 8} + } + save() + end +end + +----------------------------------------- Printer-related cyka ----------------------------------------- + +local function getPrinters() + printers = {} + for address in pairs(component.list("printer3d")) do table.insert(printers, component.proxy(address)) end +end + +local function addShapePixel(x, y, color, xPrinterPixel, yPrinterPixel) + local pixelSize = math.floor(16 / shapeResolutionLimit) + local xPrinter = x * pixelSize - pixelSize + local yPrinter = y * pixelSize - pixelSize + + if config.floorMode then + printers[currentPrinter].addShape(xPrinter, 0, yPrinter, xPrinter + pixelSize, 16, yPrinter + pixelSize, config.mainMaterial, false, color) + else + if config.frame.enabled then + local xModifyer1, xModifyer2, yModifyer1, yModifyer2 = 0, 0, 0, 0 + if xPrinterPixel == 1 then xModifyer1 = config.frame.width end + if xPrinterPixel == image.getWidth(mainImage) then xModifyer2 = -config.frame.width end + if yPrinterPixel == 1 then yModifyer2 = -config.frame.width end + if yPrinterPixel == image.getHeight(mainImage) * 2 then yModifyer1 = config.frame.width end + printers[currentPrinter].addShape(xPrinter + xModifyer1, yPrinter + yModifyer1, 15, xPrinter + pixelSize + xModifyer2, yPrinter + pixelSize + yModifyer2, 16, config.mainMaterial, false, color) + else + printers[currentPrinter].addShape(xPrinter, 15, yPrinter, xPrinter + pixelSize, 16, yPrinter + pixelSize, config.mainMaterial, false, color) + end + end +end + +local function beginPrint() + screen.clear(0x0000000, 50) + + local xShape, yShape = 1, 1 + local xShapeCount, yShapeCount = math.ceil(image.getWidth(mainImage) / shapeResolutionLimit), math.ceil(image.getHeight(mainImage) * 2 / shapeResolutionLimit) + local counter = 0 + while true do + if printers[currentPrinter].status() == "idle" then + printers[currentPrinter].reset() + printers[currentPrinter].setLabel(config.printName) + printers[currentPrinter].setTooltip("Part " .. xShape .. "x" .. yShape .. " of " .. xShapeCount .. "x" .. yShapeCount) + if config.lightEmission.enabled then printers[currentPrinter].setLightLevel(config.lightEmission.level) end + + local jReplacer = shapeResolutionLimit + for j = 1, shapeResolutionLimit / 2 do + for i = 1, shapeResolutionLimit do + local xImage = xShape * shapeResolutionLimit - shapeResolutionLimit + i + local yImage = yShape * (shapeResolutionLimit / 2) - (shapeResolutionLimit / 2) + j + + if xImage <= image.getWidth(mainImage) and yImage <= image.getHeight(mainImage) then + local background, foreground, alpha, symbol = image.get(mainImage, xImage, yImage) + if alpha < 0xFF then + if symbol == " " then foreground = background end + addShapePixel(i, jReplacer, background, xImage, yImage * 2 - 1) + addShapePixel(i, jReplacer - 1, foreground, xImage, yImage * 2) + end + + GUI.progressBar(math.floor(screen.getWidth() / 2 - 25), math.floor(screen.getHeight() / 2), 50, 0x3366CC, 0xFFFFFF, 0xFFFFFF, math.ceil(counter * 100 / (xShapeCount * yShapeCount)), true, true, "Progress: ", "%"):draw() + screen.update() + -- else + -- error("Printing out of mainImage range") + end + end + + jReplacer = jReplacer - 2 + end + + if config.frame.enabled and not config.floorMode then + local xFrame, yFrame = shapeResolutionLimit * (image.getWidth(mainImage) % shapeResolutionLimit), shapeResolutionLimit * ((image.getHeight(mainImage) * 2) % shapeResolutionLimit) + xFrame = xShape == xShapeCount and (xFrame == 0 and 16 or xFrame) or 16 + yFrame = yShape == yShapeCount and (yFrame == 0 and 0 or yFrame) or 0 + + if xShape == 1 then printers[currentPrinter].addShape(0, yFrame, 14, config.frame.width, 16, 16, config.frame.material) end + if xShape == xShapeCount then printers[currentPrinter].addShape(xFrame - config.frame.width, yFrame, 14, xFrame, 16, 16, config.frame.material) end + + if yShape == 1 then printers[currentPrinter].addShape(0, 16 - config.frame.width, 14, xFrame, 16, 16, config.frame.material) end + if yShape == yShapeCount then printers[currentPrinter].addShape(0, yFrame, 14, xFrame, yFrame + config.frame.width, 16, config.frame.material) end + end + + printers[currentPrinter].commit() + + counter = counter + 1 + xShape = xShape + 1 + if xShape > xShapeCount then xShape = 1; yShape = yShape + 1 end + if yShape > yShapeCount then break end + end + + currentPrinter = currentPrinter + 1 + if currentPrinter > #printers then currentPrinter = 1 end + event.sleep(timeDelay) + end + + screen.clear() + workspace:draw() +end + +----------------------------------------- Window-zaluped parasha ----------------------------------------- + +local function getStatus() + local xBlocks, yBlocks = math.ceil(image.getWidth(mainImage) / shapeResolutionLimit), math.ceil(image.getHeight(mainImage) * 2 / shapeResolutionLimit) + workspace.shadeContainer.statusTextBox.lines = { + "Image size: " .. image.getWidth(mainImage) .. "x" .. image.getHeight(mainImage) .. " px", + "Count of printers: " .. #printers, + "Print result: " .. xBlocks .. "x" .. yBlocks .. " blocks", + "Total count: " .. xBlocks * yBlocks .. " blocks" + } +end + +local function verticalLine(x, y, height, transparency) + for i = y, y + height - 1 do + local background = screen.get(x, i) + screen.set(x, i, background, color.blend(background, 0xFFFFFF, transparency), "│") + end +end + +local function horizontalLine(x, y, width, transparency) + for i = x, x + width - 1 do + local background, foreground, symbol = screen.get(i, y) + screen.set(i, y, background, color.blend(background, 0xFFFFFF, transparency), symbol == "│" and "┼" or "─") + end +end + +local function drawMainImageObject(object) + if mainImage then + local xImage = image.getWidth(mainImage) < screen.getWidth() and math.floor(screen.getWidth() / 2 - image.getWidth(mainImage) / 2) or 1 + local yImage = image.getHeight(mainImage) < screen.getHeight() and math.floor(screen.getHeight() / 2 - image.getHeight(mainImage) / 2) or 1 + screen.drawImage(xImage, yImage, mainImage) + GUI.drawShadow(xImage, yImage, image.getWidth(mainImage), image.getHeight(mainImage), 50, true) + if config.showGrid then + for x = xImage, xImage + image.getWidth(mainImage) - 1, shapeResolutionLimit do verticalLine(x, yImage, image.getHeight(mainImage), 0.627) end + for y = yImage, yImage + image.getHeight(mainImage) - 1, shapeResolutionLimit / 2 do horizontalLine(xImage, y, image.getWidth(mainImage), 0.627) end + screen.drawText(1, 1, 0xBBBBBB, "хуй") + end + end +end + +local function createWindow() + workspace = GUI.workspace() + workspace:addChild(GUI.panel(1, 1, workspace.width, workspace.height, 0xEEEEEE)) + workspace:addChild(GUI.object(1, 1, workspace.width, workspace.height)).draw = drawMainImageObject + local textBoxesWidth = math.floor(panelWidth * 0.55) + + workspace.shadeContainer = workspace:addChild(GUI.container(workspace.width - panelWidth + 1, 1, panelWidth, workspace.height)) + workspace.shadeContainer:addChild(GUI.panel(1, 1, workspace.shadeContainer.width, workspace.shadeContainer.height, 0x0000000, 0.4)) + + local y = 2 + workspace.shadeContainer:addChild(GUI.label(1, y, workspace.shadeContainer.width, 1, 0xFFFFFF, "Main properties")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + + y = y + 2 + workspace.shadeContainer:addChild(GUI.label(3, y, workspace.shadeContainer.width, 1, 0xCCCCCC, "Image path:")) + local filesystemChooser = workspace.shadeContainer:addChild(GUI.filesystemChooser(workspace.shadeContainer.width - textBoxesWidth - 1, y, textBoxesWidth, 1, 0xEEEEEE, 0x262626, 0x444444, 0x999999, startImagePath, system.localization.open, system.localization.cancel, "Image path", "/")) + filesystemChooser:addExtensionFilter(".pic") + filesystemChooser.onSubmit = function(path) + mainImage = image.load(path) + getStatus() + workspace:draw() + end + + y = y + 2 + workspace.shadeContainer:addChild(GUI.label(3, y, workspace.shadeContainer.width, 1, 0xCCCCCC, "Material:")) + local mainMaterialTextBox = workspace.shadeContainer:addChild(GUI.input(workspace.shadeContainer.width - textBoxesWidth - 1, y, textBoxesWidth, 1, 0xEEEEEE, 0x555555, 0x555555, 0xEEEEEE, 0x262626, config.mainMaterial, nil, false)) + mainMaterialTextBox.onInputFinished = function() + config.mainMaterial = mainMaterialTextBox.text + save() + end + + y = y + 2 + workspace.shadeContainer:addChild(GUI.label(3, y, workspace.shadeContainer.width, 1, 0xCCCCCC, "Print name:")) + local printNameTextBox = workspace.shadeContainer:addChild(GUI.input(workspace.shadeContainer.width - textBoxesWidth - 1, y, textBoxesWidth, 1, 0xEEEEEE, 0x555555, 0x555555, 0xEEEEEE, 0x262626, config.printName, nil, false)) + printNameTextBox.onInputFinished = function() + config.printName = printNameTextBox.text + save() + end + + y = y + 2 + workspace.shadeContainer:addChild(GUI.label(3, y, workspace.shadeContainer.width, 1, 0xCCCCCC, "Floor mode:")) + local floorSwitch = workspace.shadeContainer:addChild(GUI.switch(workspace.shadeContainer.width - 9, y, 8, 0xFFDB40, 0xAAAAAA, 0xFFFFFF, config.floorMode)) + floorSwitch.onStateChanged = function() + config.floorMode = floorSwitch.state + save() + end + + y = y + 2 + workspace.shadeContainer:addChild(GUI.label(3, y, workspace.shadeContainer.width, 1, 0xCCCCCC, "Show grid:")) + local gridSwitch = workspace.shadeContainer:addChild(GUI.switch(workspace.shadeContainer.width - 9, y, 8, 0xFFDB40, 0xAAAAAA, 0xFFFFFF, config.showGrid)) + gridSwitch.onStateChanged = function() + config.showGrid = gridSwitch.state + save() + workspace:draw() + end + + y = y + 4 + workspace.shadeContainer:addChild(GUI.label(1, y, workspace.shadeContainer.width, 1, 0xFFFFFF, "Frame properties")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + y = y + 2 + workspace.shadeContainer:addChild(GUI.label(3, y, workspace.shadeContainer.width, 1, 0xCCCCCC, "Enabled:")) + local frameSwitch = workspace.shadeContainer:addChild(GUI.switch(workspace.shadeContainer.width - 9, y, 8, 0xFFDB40, 0xAAAAAA, 0xFFFFFF, config.frame.enabled)) + frameSwitch.onStateChanged = function() + config.frame.enabled = frameSwitch.state + save() + end + y = y + 2 + workspace.shadeContainer:addChild(GUI.label(3, y, workspace.shadeContainer.width, 1, 0xCCCCCC, "Material:")) + local frameMaterialTextBox = workspace.shadeContainer:addChild(GUI.input(workspace.shadeContainer.width - textBoxesWidth - 1, y, textBoxesWidth, 1, 0xEEEEEE, 0x555555, 0x555555, 0xEEEEEE, 0x262626, config.frame.material, nil, false)) + frameMaterialTextBox.onInputFinished = function() + config.frame.material = frameMaterialTextBox.text + save() + end + + y = y + 2 + local frameWidthSlider = workspace.shadeContainer:addChild(GUI.slider(3, y, workspace.shadeContainer.width - 4, 0xFFDB80, 0x000000, 0xFFDB40, 0xCCCCCC, 1, shapeResolutionLimit - 1, config.frame.width, false, "Width: " , " voxel(s)")) + frameWidthSlider.onValueChanged = function() + config.frame.width = frameWidthSlider.value + save() + end + frameWidthSlider.roundValues = true + + y = y + 5 + workspace.shadeContainer:addChild(GUI.label(1, y, workspace.shadeContainer.width, 1, 0xFFFFFF, "Light emission")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + y = y + 2 + workspace.shadeContainer:addChild(GUI.label(3, y, workspace.shadeContainer.width, 1, 0xCCCCCC, "Enabled:")) + local lightSwitch = workspace.shadeContainer:addChild(GUI.switch(workspace.shadeContainer.width - 9, y, 8, 0xFFDB40, 0xAAAAAA, 0xFFFFFF, config.lightEmission.enabled)) + lightSwitch.onStateChanged = function() + config.lightEmission.enabled = true + save() + end + + y = y + 2 + local lightSlider = workspace.shadeContainer:addChild(GUI.slider(3, y, workspace.shadeContainer.width - 4, 0xFFDB80, 0x000000, 0xFFDB40, 0xCCCCCC, 1, 8, 8, false, "Radius: " , " block(s)")) + lightSlider.roundValues = true + lightSlider.onValueChanged = function() + config.lightEmission.value = lightSlider.value + save() + end + + y = y + 5 + workspace.shadeContainer:addChild(GUI.label(1, y, workspace.shadeContainer.width, 1, 0xFFFFFF, "Summary information:")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + y = y + 2 + workspace.shadeContainer.statusTextBox = workspace.shadeContainer:addChild(GUI.textBox(3, y, workspace.shadeContainer.width - 4, 5, nil, 0xCCCCCC, {}, 1)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP) + + workspace.shadeContainer:addChild(GUI.button(1, workspace.shadeContainer.height - 5, workspace.shadeContainer.width, 3, 0x363636, 0xFFFFFF, 0xFFFFFF, 0x262626, "Exit")).onTouch = function() + workspace:stop() + end + + workspace.shadeContainer:addChild(GUI.button(1, workspace.shadeContainer.height - 2, workspace.shadeContainer.width, 3, 0x262626, 0xFFFFFF, 0xFFFFFF, 0x262626, "Start print")).onTouch = function() + beginPrint() + end + + workspace.eventHandler = function(workspace, object, e1, e2, e3) + if (e1 == "component_added" or e1 == "component_removed") and e3 == "printer3d" then + getPrinters() + getStatus() + workspace:draw() + end + end +end + +----------------------------------------- Shitty meatball rolls ----------------------------------------- + +screen.flush() +load() +getPrinters() +createWindow() +mainImage = image.load(startImagePath) +getStatus() + +workspace:draw() +workspace:start() diff --git a/Applications/QuantumCube/About/English.txt b/Applications/QuantumCube/About/English.txt deleted file mode 100644 index 5afb8118..00000000 --- a/Applications/QuantumCube/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Это мини-игра, разработанная товарищем qwertyMan с форума ComputerCraft.ru. А я ее нагло спиздил. Цель игры: вы должны понять, как устроен "квантовый куб", решить задачу (найти цепочку выходов) к комнате номер 1 и выбраться из квантового лабиринта. А на деле рандомно бегать в поисках комнаты номер 1, не понимать как устроена система нумераций, ловить баттхёрты и проклинать всех, кого только можно. Потому что если даже соседняя комната и окажется под номером 1, то вы можете запросто пробежать и даже не заглянуть в неё. Так как мы видим лишь те комнаты, на границе с которыми стоим. \ No newline at end of file diff --git a/Applications/QuantumCube/About/Russian.txt b/Applications/QuantumCube/About/Russian.txt deleted file mode 100644 index 5afb8118..00000000 --- a/Applications/QuantumCube/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Это мини-игра, разработанная товарищем qwertyMan с форума ComputerCraft.ru. А я ее нагло спиздил. Цель игры: вы должны понять, как устроен "квантовый куб", решить задачу (найти цепочку выходов) к комнате номер 1 и выбраться из квантового лабиринта. А на деле рандомно бегать в поисках комнаты номер 1, не понимать как устроена система нумераций, ловить баттхёрты и проклинать всех, кого только можно. Потому что если даже соседняя комната и окажется под номером 1, то вы можете запросто пробежать и даже не заглянуть в неё. Так как мы видим лишь те комнаты, на границе с которыми стоим. \ No newline at end of file diff --git a/Applications/QuantumCube/Icon.pic b/Applications/QuantumCube/Icon.pic deleted file mode 100644 index a6643e546702915c6dac42b5117a67e7907d3e9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 97 zcmW-X(G7qw5JC^`tR2JLDkdg=xl%uzp#!#yi2Z=TgXg@jQUhi_dV?{EpqHIQop8oB b`YS#`<)n+pdK^q~{9Q6wKo73XmxuZXKQ0Jd diff --git a/Applications/QuantumCube/QuantumCube.lua b/Applications/QuantumCube/QuantumCube.lua deleted file mode 100644 index 726b160e..00000000 --- a/Applications/QuantumCube/QuantumCube.lua +++ /dev/null @@ -1,442 +0,0 @@ -local os = require("os") -local term = require("term") -local event = require("event") -local component = require("component") -local gpu = component.gpu -math.randomseed(os.time()) -local version = "Cube v1.0" -local level = {} -local doors = {} -local indicator={} -local nick_player -local markColor = {0xff0000, 0xffff00, 0x00ff00, 0x00ffff, 0x0000ff, 0xff00ff, 0xffffff} -local playerColor = 3 -local sx,sy = gpu.getResolution() -sx, sy = math.modf(sx/4), math.modf(sy/2) -- точка отступа/координата игрока на экране (статичные)/центр событий -local px,py = 3,3 -- относительные координаты игрока в комнате -local min_map, max_map = 1, 1000 -- ограничители номеров комнат, чтобы сильно не запутаться при прохождении -level[1] = {number=1, color = 0xffffff, mark=false} -- цель игры - попасть в эту комнату -------------------------------------------------------------------------------------- ------------------------ Генерация случайных формул для дверей ----------------------- -local rand_1, rand_2 ,rand_3, rand_4 -while true do - rand_1, rand_2 ,rand_3, rand_4 = math.random(1,2), math.random(10,30), math.random(1,2), math.random(10,30) - if rand_1~=rand_3 and rand_2~=rand_4 then break end -end -formula={} -formula[1]=function(n) return n*rand_1 + rand_2 end -formula[2]=function(n) return n*rand_3 + rand_4 end -formula[-1]=function(n) return (n - rand_2)/rand_1 end -formula[-2]=function(n) return (n - rand_4)/rand_3 end -------------------------------------------------------------------------------------- ----------------------- Возвращает или генерирует новую комнату ---------------------- -function gen_level(i) - i = tonumber(i) - if not level[i] then - level[i]={number=i, color = math.random(0x000000, 0xffffff), mark=false} - end - return level[i] -end -------------------------------------------------------------------------------------- --------------------------- Проверка, существует ли комната -------------------------- -function proverka(x,y) -- где x номер формулы, y номер текущей комнаты - local number = formula[x](y) - return number >= min_map and number <= max_map and number == math.modf(number) -end -------------------------------------------------------------------------------------- ----------------------------- Генерация статистики комнат ---------------------------- -function sorting(sorting_table, not_sorting_table) - local trash_table={} - while true do - if #not_sorting_table==0 then - break - else - local new_level = not_sorting_table[1] - local power = true - for i=1, #sorting_table do - if sorting_table[i][1]== new_level[1] then - power = false - if new_level[2] < sorting_table[i][2] then - sorting_table[i][2]=new_level[2] - if proverka(1,new_level[1]) then - trash_table[#trash_table+1] = {formula[1](new_level[1]), new_level[2]+1} - end - if proverka(2,new_level[1]) then - trash_table[#trash_table+1] = {formula[2](new_level[1]), new_level[2]+1} - end - if proverka(-1,new_level[1]) then - trash_table[#trash_table+1] = {formula[-1](new_level[1]), new_level[2]+1} - end - if proverka(-2,new_level[1]) then - trash_table[#trash_table+1] = {formula[-2](new_level[1]), new_level[2]+1} - end - end - table.remove(not_sorting_table,1) - end - end - if power then - sorting_table[#sorting_table+1] = new_level - table.remove(not_sorting_table,1) - if proverka(1,new_level[1]) then - not_sorting_table[#not_sorting_table+1] = {formula[1](new_level[1]), new_level[2]+1} - end - if proverka(2,new_level[1]) then - not_sorting_table[#not_sorting_table+1] = {formula[2](new_level[1]), new_level[2]+1} - end - if proverka(-1,new_level[1]) then - not_sorting_table[#not_sorting_table+1] = {formula[-1](new_level[1]), new_level[2]+1} - end - if proverka(-2,new_level[1]) then - not_sorting_table[#not_sorting_table+1] = {formula[-2](new_level[1]), new_level[2]+1} - end - end - end - end - return sorting_table, trash_table -end - --- первая сортировка -local not_sorting_tb, trash_tb={} -not_sorting_tb[1]={1,0} -local sorting_tb, trash_tb = sorting({}, not_sorting_tb) - --- последующие сортировки -while true do - not_sorting_tb = trash_tb - sorting_tb, trash_tb = sorting(sorting_tb, not_sorting_tb) - if #trash_tb == 0 then break end -end - --- очищаем память -not_sorting_tb, trash_tb = nil, nil - --- перестраиваем таблицу -local stat_table={} -for i=1, #sorting_tb do - stat_table[sorting_tb[i][1]]=sorting_tb[i][2] -end -------------------------------------------------------------------------------------- ------------------- Находим номер самой удалённой комнаты от выхода ------------------ -local j=1 -for i=1, #sorting_tb do - if sorting_tb[i][2]>sorting_tb[j][2] then - j=i - end -end -------------------------------------------------------------------------------------- ------------------------ Устанавливаем номер стартовой комнаты ----------------------- -local chamber = gen_level(sorting_tb[j][1]) - --- запишем количество комнат в игре -local max_table, max_level = #sorting_tb, sorting_tb[j][2] - --- удалим из памяти ненужную таблицу -sorting_tb = nil -------------------------------------------------------------------------------------- ---------------------------- Переставляет двери в комнате ---------------------------- -function reload_doors() - local rezerv={} - for i=1,4 do -- занесём двери в базу данных, чтобы знать использованные формулы - if doors[i] then - rezerv[#rezerv+1]=doors[i] - end - end - for i=1,4 do -- перебираем все 4 двери - if not doors[i] then - while true do - local rand = math.random(-2,2) - local rezerv_2 = 0 - if rand ~= 0 then - if #rezerv > 0 then -- проверка, есть ли комната с такой же формулой - for j=1, #rezerv do - if rezerv[j] == rand then break else rezerv_2 = rezerv_2 + 1 end - end - end - if rezerv_2 == #rezerv then -- если нет повторяющихся формул, то присваивается данная формула - doors[i] = rand - rezerv[#rezerv+1]=rand - break - end - end - end - end - end -end --- //данная функция достаточно сложна чтобы запутаться -------------------------------------------------------------------------------------- ----------------------------------- Рисования меток ---------------------------------- -function mark_print(nx, ny, number) - if level[number].mark then - for i=1, #level[number].mark do - gpu.setBackground(level[number].mark[i][3]) - gpu.set((level[number].mark[i][1]+nx)*2, level[number].mark[i][2]+ny, " ") - end - end -end -------------------------------------------------------------------------------------- -------------------------- Рисования комнаты по координатам -------------------------- -function level_print(nx, ny, color, number) - number = tostring(number) - gpu.setBackground(color) - gpu.set(nx*2, ny, " ") - gpu.set((nx+4)*2, ny, " ") - gpu.set(nx*2, ny+6, " ") - gpu.set((nx+4)*2, ny+6, " ") - - gpu.set(nx*2, ny+1, " ") - gpu.set(nx*2, ny+2, " ") - gpu.set(nx*2, ny+4, " ") - gpu.set(nx*2, ny+5, " ") - gpu.set((nx+6)*2, ny+1, " ") - gpu.set((nx+6)*2, ny+2, " ") - gpu.set((nx+6)*2, ny+4, " ") - gpu.set((nx+6)*2, ny+5, " ") - - gpu.setBackground(0x000000) - gpu.set(nx*2+6-math.modf((string.len(number)-1)/2), ny+3, number) -end -------------------------------------------------------------------------------------- ------------------------------ Переходы между комнатами ------------------------------ -pxx, pyy = {}, {} -pxx[-1]=function(nx) - local rezerv_3 = doors[1] - if proverka(rezerv_3, chamber.number) then - doors={} - doors[2] = -rezerv_3 - reload_doors() - chamber = gen_level(formula[rezerv_3](chamber.number)) - ppx = 6 - else - ppx = px - end -end -pxx[7]=function(nx) - local rezerv_3 = doors[2] - if proverka(rezerv_3, chamber.number) then - doors={} - doors[1] = -rezerv_3 - reload_doors() - chamber = gen_level(formula[rezerv_3](chamber.number)) - ppx = 0 - else - ppx = px - end -end -pyy[-1]=function(ny) - local rezerv_3 = doors[3] - if proverka(rezerv_3, chamber.number) then - doors={} - doors[4] = -rezerv_3 - reload_doors() - chamber = gen_level(formula[rezerv_3](chamber.number)) - ppy = 6 - else - ppy = py - end -end -pyy[7]=function(ny) - local rezerv_3 = doors[4] - if proverka(rezerv_3, chamber.number) then - doors={} - doors[3] = -rezerv_3 - reload_doors() - chamber = gen_level(formula[rezerv_3](chamber.number)) - ppy = 0 - else - ppy = py - end -end --- //работает как надо, но лучше подредактировать -------------------------------------------------------------------------------------- --------------------------------- Передвижение игрока -------------------------------- -function player_update(nx,ny) - ppx, ppy = px+nx, py+ny - if not ((ppx==0 or ppy==0 or ppx==6 or ppy==6) and ppx~=3 and ppy~=3) then - if pxx[ppx] then pxx[ppx](ppx) - elseif pyy[ppy] then pyy[ppy](ppy) - end - px,py = ppx,ppy - end -end --- //работает как надо, но лучше подредактировать -------------------------------------------------------------------------------------- ---------------------------------- Блок отображения ---------------------------------- -function update(nick) - nick_player = nick - term.clear() - - -- текущая комната - gen_level(chamber.number) - mark_print(sx-px,sy-py, chamber.number) - level_print(sx-px,sy-py,chamber.color, chamber.number) - - -- комната слева - if proverka(doors[1], chamber.number) and px==0 then - local number = formula[doors[1]](chamber.number) - gen_level(number) - mark_print(sx-7-px,sy-py, number) - level_print(sx-7-px,sy-py,gen_level(formula[doors[1]](chamber.number)).color, gen_level(formula[doors[1]](chamber.number)).number) - end - - -- комната справа - if proverka(doors[2], chamber.number) and px==6 then - local number = formula[doors[2]](chamber.number) - gen_level(number) - mark_print(sx+7-px,sy-py, number) - level_print(sx+7-px,sy-py,gen_level(formula[doors[2]](chamber.number)).color, gen_level(formula[doors[2]](chamber.number)).number) - end - - -- комната сверху - if proverka(doors[3], chamber.number) and py==0 then - local number = formula[doors[3]](chamber.number) - gen_level(number) - mark_print(sx-px,sy-7-py, number) - level_print(sx-px,sy-7-py,gen_level(formula[doors[3]](chamber.number)).color, gen_level(formula[doors[3]](chamber.number)).number) - end - - -- комната снизу - if proverka(doors[4], chamber.number) and py==6 then - local number = formula[doors[4]](chamber.number) - gen_level(number) - mark_print(sx-px,sy+7-py, number) - level_print(sx-px,sy+7-py,gen_level(formula[doors[4]](chamber.number)).color, number) - end - - -- отображение игрока - gpu.setBackground(0xff0000) - gpu.set(sx*2, sy, " ") - - -- текстовые индикаторы - for i=1, #indicator do - indicator[i]() - end - - -- индикатор выбранного цвета - gpu.setBackground(markColor[playerColor]) - gpu.set(2, sy*2, " ") -end -------------------------------------------------------------------------------------- ----------------------------------- Блок управления ---------------------------------- -local command={} -command[200]=function() player_update(0,-1) end -- вверх -command[208]=function() player_update(0,1) end -- вниз -command[203]=function() player_update(-1,0) end -- влево -command[205]=function() player_update(1,0) end -- вправо -command[17]=function() -- ставить метку - if not level[chamber.number].mark then level[chamber.number].mark={} end - level[chamber.number].mark[#level[chamber.number].mark+1]={px,py,markColor[playerColor]} - end -command[30]=function() if playerColor-1<1 then playerColor=#markColor else playerColor=playerColor-1 end end -- цвет слева -command[32]=function() if playerColor+1>#markColor then playerColor=1 else playerColor=playerColor+1 end end -- цвет справа -command[31]=function() -- удалить метку - if level[chamber.number].mark then - for i=#level[chamber.number].mark, 1, -1 do - if px==level[chamber.number].mark[i][1] and py==level[chamber.number].mark[i][2] then - table.remove(level[chamber.number].mark,i) - end - end - end - end -command[23]=function() -- включает режим разработчика - if #indicator==0 then - indicator[1]=function() - gpu.setBackground(0x000000) - gpu.setForeground(0xffff00) - gpu.set(2, 2, "max level: "..max_level) - gpu.set(2, 3, "all levels: "..max_table) - gpu.set(2, 4, "this level: "..stat_table[chamber.number]) - gpu.setForeground(0xff0000) - gpu.set(2, 5, "formula 1: ".."n".."*"..rand_1.." + "..rand_2) - gpu.set(2, 6, "formula 2: ".."n".."*"..rand_3.." + "..rand_4) - gpu.set(2, 7, "formula -1: ".."(n - "..rand_2..")/"..rand_1) - gpu.set(2, 8, "formula -2: ".."(n - "..rand_4..")/"..rand_3) - gpu.setForeground(0xff00ff) - gpu.set(2, 9, "progress: " .. math.modf(100-stat_table[chamber.number]*100/max_level).."%") - gpu.setForeground(0xff00ff) - gpu.set(2, 10, "color: "..playerColor) - gpu.setForeground(0xffffff) - end - else - table.remove(indicator,1) - end -end -command[35]=function() -- отобразить управление - term.clear() - gpu.setForeground(0xff0000) - print(version) - print("") - gpu.setForeground(0x00ff00) - print("Target: searching chamber 1") - print("Game 100% passable") - gpu.setForeground(0xffff00) - print("Control:") - print("Q - exit game") - print("H - help") - print("I - info") - print("W - set mark") - print("S - remove mark") - print("A - back color mark") - print("D - next color mark") - print("") - gpu.setForeground(0xffffff) - print("press enter to continue...") - while true do - _,_,_, key = event.pull("key_down") - if key == 28 then - break - end - end -end -------------------------------------------------------------------------------------- ----------------------- Показываем управление до начала игры ------------------------- -command[35]() -------------------------------------------------------------------------------------- -------------------------- Отображение комнат до начала игры ------------------------- -reload_doors() -update(nick) -gpu.setBackground(0x000000) -------------------------------------------------------------------------------------- -------------------------------------- Тело игры ------------------------------------- -while true do - _,_,_, key, nick = event.pull("key_down") - if key==16 then - term.clear() - gpu.setForeground(0xff0000) - print("Exit to game?") - gpu.setForeground(0xffffff) - print("") - print("y/n") - while true do - _,_,_, key = event.pull("key_down") - if key == 21 then - term.clear() - return - elseif key == 49 then - break - end - end - elseif command[key] then - command[key]() - end - update(nick) - gpu.setBackground(0x000000) - if chamber.number == 1 then break end -- цель игры, комната с этим номером - os.sleep(1/15) -- задержка, для более удобного управления -end -------------------------------------------------------------------------------------- -------------------------------------- Прощание ------------------------------------- -term.clear() -gpu.setForeground(0x00ff00) -print("Congratulations "..nick_player.."!") -print("You win!") -print("") -gpu.setForeground(0xffffff) -print("press enter to exit...") -while true do - _,_,_, key = event.pull("key_down") - if key == 28 then - break - end -end -term.clear() -------------------------------------------------------------------------------------- \ No newline at end of file diff --git a/Applications/Radio/About/English.txt b/Applications/Radio/About/English.txt deleted file mode 100644 index e96c4609..00000000 --- a/Applications/Radio/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Программа для управления радио из мода OpenFM, стилизованная под известный плеер iRiver SPINN. \ No newline at end of file diff --git a/Applications/Radio/About/Russian.txt b/Applications/Radio/About/Russian.txt deleted file mode 100644 index e96c4609..00000000 --- a/Applications/Radio/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Программа для управления радио из мода OpenFM, стилизованная под известный плеер iRiver SPINN. \ No newline at end of file diff --git a/Applications/Radio/Icon.pic b/Applications/Radio/Icon.pic deleted file mode 100644 index c5f17e38f4de143b492944eee9c33c298e9fd88f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 167 zcmXxcxebI+3`NoZZ2u*q22w(n5Rr^D(nLtm0>ns^Kn+j|4N(FOfCEo #radioStations then - radioStations.currentStation = #radioStations - end - - buffer.square(1, 1, buffer.getWidth(), buffer.getHeight(), config.colors.background, 0xFFFFFF, " ") - - drawStations() - drawLine() - drawMenu() - - buffer.draw() -end - -local function saveStations() - fs.makeDirectory(fs.path(pathToSaveStations)) - local file = io.open(pathToSaveStations, "w") - file:write(serialization.serialize(radioStations)) - file:close() -end - -local function loadStations() - if fs.exists(pathToSaveStations) then - local file = io.open(pathToSaveStations, "r") - radioStations = serialization.unserialize(file:read("*a")) - file:close() - else - saveStations() - end -end - -local function switchStation(i) - if i == 1 then - if radioStations.currentStation < #radioStations then - radioStations.currentStation = radioStations.currentStation + 1 - saveStations() - radio.stop() - radio.setURL(radioStations[radioStations.currentStation].url) - radio.start() - end - else - if radioStations.currentStation > 1 then - radioStations.currentStation = radioStations.currentStation - 1 - saveStations() - radio.stop() - radio.setURL(radioStations[radioStations.currentStation].url) - radio.start() - end - end -end - -local function volume(i) - if i == 1 then - radio.volUp() - else - radio.volDown() - end -end - - -buffer.flush() -lineHeight = math.floor(buffer.getHeight() * 0.7) -loadStations() -radio.stop() -radio.setURL(radioStations[radioStations.currentStation].url) -radio.start() -drawAll() - -while true do - local e = {event.pull()} - if e[1] == "touch" then - if e[5] == 0 then - if ecs.clickedAtArea(e[3], e[4], obj.strelkaVlevo[1], obj.strelkaVlevo[2], obj.strelkaVlevo[3], obj.strelkaVlevo[4]) then - drawLeftArrow(obj.strelkaVlevo[1], obj.strelkaVlevo[2], config.colors.bottomToolBarCurrentColor) - buffer.draw() - os.sleep(0.2) - switchStation(-1) - drawAll() - elseif ecs.clickedAtArea(e[3], e[4], obj.strelkaVpravo[1], obj.strelkaVpravo[2], obj.strelkaVpravo[3], obj.strelkaVpravo[4]) then - drawRightArrow(obj.strelkaVpravo[1], obj.strelkaVpravo[2], config.colors.bottomToolBarCurrentColor) - buffer.draw() - os.sleep(0.2) - switchStation(1) - drawAll() - elseif ecs.clickedAtArea(e[3], e[4], obj.gromkostPlus[1], obj.gromkostPlus[2], obj.gromkostPlus[3], obj.gromkostPlus[4]) then - bigLetters.drawText(obj.gromkostPlus[1], obj.gromkostPlus[2], config.colors.bottomToolBarCurrentColor, "+", "*" ) - buffer.draw() - volume(1) - os.sleep(0.2) - drawAll() - elseif ecs.clickedAtArea(e[3], e[4], obj.gromkostMinus[1], obj.gromkostMinus[2], obj.gromkostMinus[3], obj.gromkostMinus[4]) then - bigLetters.drawText(obj.gromkostMinus[1], obj.gromkostMinus[2], config.colors.bottomToolBarCurrentColor, "-", "*" ) - buffer.draw() - volume(-1) - os.sleep(0.2) - drawAll() - end - else - local action = context.menu(e[3], e[4], {"Добавить станцию", #radioStations >= countOfStationsLimit}, {"Удалить станцию", #radioStations < 2}, "-", {"О программе"}, "-", {"Выход"}) - if action == "Добавить станцию" then - local data = ecs.universalWindow("auto", "auto", 36, 0x262626, true, - {"EmptyLine"}, - {"CenterText", ecs.colors.orange, "Добавить станцию"}, - {"EmptyLine"}, - {"Input", 0xFFFFFF, ecs.colors.orange, "Название станции"}, - {"Input", 0xFFFFFF, ecs.colors.orange, "URL-ссылка на стрим"}, - {"EmptyLine"}, - {"Button", {ecs.colors.orange, 0x262626, "OK"}, {0x999999, 0xffffff, "Отмена"}} - ) - if data[3] == "OK" then - table.insert(radioStations, {name = data[1], url = data[2]}) - saveStations() - drawAll() - end - elseif action == "Удалить станцию" then - table.remove(radioStations, radioStations.currentStation) - saveStations() - drawAll() - - elseif action == "О программе" then - ecs.universalWindow("auto", "auto", 36, 0x262626, true, - {"EmptyLine"}, - {"CenterText", ecs.colors.orange, "Radio v1.0"}, - {"EmptyLine"}, - {"CenterText", 0xFFFFFF, "Автор:"}, - {"CenterText", 0xBBBBBB, "Тимофеев Игорь"}, - {"CenterText", 0xBBBBBB, "vk.com/id7799889"}, - {"EmptyLine"}, - {"CenterText", 0xFFFFFF, "Тестер:"}, - {"CenterText", 0xBBBBBB, "Олег Гречкин"}, - {"CenterText", 0xBBBBBB, "http://vk.com/id250552893"}, - {"EmptyLine"}, - {"CenterText", 0xFFFFFF, "Автор идеи:"}, - {"CenterText", 0xBBBBBB, "MrHerobrine с Dreamfinity"}, - {"EmptyLine"}, - {"Button", {ecs.colors.orange, 0xffffff, "OK"}} - ) - elseif action == "Выход" then - buffer.square(1, 1, buffer.getWidth(), buffer.getHeight(), config.colors.background, 0xFFFFFF, " ") - buffer.draw() - ecs.prepareToExit() - radio.stop() - return - end - end - - elseif e[1] == "scroll" then - switchStation(e[5]) - drawAll() - end -end - - - - - - - diff --git a/Applications/RayWalk.app/Icon.pic b/Applications/RayWalk.app/Icon.pic new file mode 100755 index 0000000000000000000000000000000000000000..b8e59198110608e957eb9e058ab6b45a326303e2 GIT binary patch literal 101 zcmXxbu?>JA6h%?)|6gDXcQG-svSA= 40 and num <= 160 then return true end end - resolutionTextBoxHeight.validator = function(text) local num = tonumber(text); if num and num >= 12 and num <= 50 then return true end end - local function onAnyResolutionTextBoxInputFinished() - window:stop() - rayEngine.changeResolution(tonumber(resolutionTextBoxWidth.text), tonumber(resolutionTextBoxHeight.text)) - settings() - end - resolutionTextBoxWidth.onInputFinished = onAnyResolutionTextBoxInputFinished - resolutionTextBoxHeight.onInputFinished = onAnyResolutionTextBoxInputFinished - - local drawDistanceSlider = window:addChild(GUI.slider(x, y, sliderWidth, 0xFFDB80, 0x000000, 0xFFDB40, 0xDDDDDD, 100, 5000, rayEngine.properties.drawDistance, true, localization.drawDistance)) - drawDistanceSlider.onValueChanged = function() - rayEngine.properties.drawDistance = drawDistanceSlider.value - window:draw() - end; y = y + 4 - - local shadingDistanceSlider = window:addChild(GUI.slider(x, y, sliderWidth, 0xFFDB80, 0x000000, 0xFFDB40, 0xDDDDDD, 100, 3000, rayEngine.properties.shadingDistance, true, localization.shadingDistance)) - shadingDistanceSlider.onValueChanged = function() - rayEngine.properties.shadingDistance = shadingDistanceSlider.value - window:draw() - end; y = y + 4 - - local shadingCascadesSlider = window:addChild(GUI.slider(x, y, sliderWidth, 0xFFDB80, 0x000000, 0xFFDB40, 0xDDDDDD, 2, 48, rayEngine.properties.shadingCascades, true, localization.shadingCascades)) - shadingCascadesSlider.onValueChanged = function() - rayEngine.properties.shadingCascades = shadingCascadesSlider.value - window:draw() - end; y = y + 4 - - local raycastQualitySlider = window:addChild(GUI.slider(x, y, sliderWidth, 0xFFDB80, 0x000000, 0xFFDB40, 0xDDDDDD, 0.5, 32, rayEngine.properties.raycastQuality, true, localization.raycastQuality)) - raycastQualitySlider.onValueChanged = function() - rayEngine.properties.raycastQuality = raycastQualitySlider.value - window:draw() - end; y = y + 4 - - local currentTimeSlider = window:addChild(GUI.slider(x, y, sliderWidth, rayEngine.world.colors.sky.current, 0x000000, rayEngine.world.colors.sky.current, 0xDDDDDD, 0, rayEngine.world.dayNightCycle.length, rayEngine.world.dayNightCycle.currentTime, true, localization.dayNightCycle, localization.seconds)) - currentTimeSlider.onValueChanged = function() - rayEngine.world.dayNightCycle.currentTime = currentTimeSlider.value - rayEngine.refreshTimeDependentColors() - currentTimeSlider.colors.active = rayEngine.world.colors.sky.current - currentTimeSlider.colors.pipe = rayEngine.world.colors.sky.current - window:draw() - end; y = y + 4 - - window:addChild(GUI.label(x, y, sliderWidth, 1, 0xDDDDDD, localization.enableSemipixelRenderer)) - - local graphonSwitch = window:addChild(GUI.switch(x + sliderWidth - 8, y, 8, 0xFFDB40, 0xAAAAAA, 0xFFFFFF, not rayEngine.properties.useSimpleRenderer)) - graphonSwitch.onStateChanged = function() - rayEngine.properties.useSimpleRenderer = not graphonSwitch.state - window:draw() - end; y = y + 3 - - window:addChild(GUI.label(x, y, sliderWidth, 1, 0xDDDDDD, localization.enableDayNightCycle)) - - local lockTimeSwitch = window:addChild(GUI.switch(x + sliderWidth - 8, y, 8, 0xFFDB40, 0xAAAAAA, 0xFFFFFF, rayEngine.world.dayNightCycle.enabled)) - lockTimeSwitch.onStateChanged = function() - rayEngine.world.dayNightCycle.enabled = lockTimeSwitch.state - window:draw() - end; y = y + 3 - - window:addChild(GUI.button(x, y, sliderWidth, 3, 0xEEEEEE, 0x262626, 0xBBBBBB, 0x262626, localization.continue)).onTouch = function() window:stop(); table.toFile(applicationResourcesDirectory .. "RayEngine.cfg", rayEngine.properties, true) end - - window:draw() - window:start() -end - -local function menu() - local window = GUI.application() - local oldDraw = window.draw - window.draw = function() - menuBackground() - oldDraw(window) - end - - local buttonWidth, buttonHeight = 50, 3 - local worlds = {} - for file in fs.list(worldsPath) do table.insert(worlds, unicode.sub(file, 1, -2)) end - local x, y = math.floor(window.width / 2 - buttonWidth / 2), math.floor(window.height / 2 - #worlds * (buttonHeight + 1) / 2 - 11) - - window:addChild(GUI.label(1, y, window.width, 1, 0xFFFFFF, rayWalkVersion)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); y = y + 3 - window:addChild(GUI.button(x, y, buttonWidth, buttonHeight, 0xEEEEEE, 0x262626, 0xBBBBBB, 0x262626, localization.continue)).onTouch = function() window:stop() end; y = y + buttonHeight + 1 - window:addChild(GUI.button(x, y, buttonWidth, buttonHeight, 0xEEEEEE, 0x262626, 0xBBBBBB, 0x262626, localization.settings)).onTouch = function() window:stop(); settings() end; y = y + buttonHeight + 1 - window:addChild(GUI.button(x, y, buttonWidth, buttonHeight, 0xEEEEEE, 0x262626, 0x999999, 0x262626, localization.exit)).onTouch = function() buffer.clear(0x000000); buffer.drawChanges(); os.exit() end; y = y + buttonHeight + 1 - window:addChild(GUI.label(1, y, window.width, 1, 0xFFFFFF, localization.loadWorld)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); y = y + 2 - - for i = 1, #worlds do - window:addChild(GUI.button(x, y, buttonWidth, buttonHeight, 0xEEEEEE, 0x262626, 0xBBBBBB, 0x262626, worlds[i])).onTouch = function() rayEngine.loadWorld(worldsPath .. worlds[i]); window:stop() end - y = y + buttonHeight + 1 - end - - local lines = {}; for i = 1, #localization.controlsHelp do table.insert(lines, localization.controlsHelp[i]) end - table.insert(lines, 1, " ") - table.insert(lines, 1, {text = localization.controls, color = 0xFFFFFF}) - window:addChild(GUI.textBox(1, y, window.width, #lines, nil, 0xCCCCCC, lines, 1):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)); y = y + #lines + 1 - - window:draw() - window:start() -end - - ----------------------------------------------------------------------------------------------------------------------------------- - -local controls = { - ["key_down"] = { - [16] = rayEngine.turnLeft, --q - [18] = rayEngine.turnRight, --e - [30] = rayEngine.moveLeft, --a - [32] = rayEngine.moveRight, --d - [17] = rayEngine.moveForward, --w - [31] = rayEngine.moveBackward, --s - [50] = rayEngine.toggleMinimap, --m - [37] = rayEngine.toggleCompass, --k - [25] = rayEngine.toggleWatch, --p - [14] = menu, --backspace - [28] = rayEngine.commandLine, --enter - [57] = rayEngine.jump, --space - [29] = rayEngine.crouch, --ctrl - [59] = rayEngine.toggleDebugInformation, -- F1 - }, - ["key_up"] = { - [29] = rayEngine.crouch, --ctrl - }, -} - --------------------------------------------------------------------------------------------------------------- - -rayEngine.loadEngineProperties(applicationResourcesDirectory .. "RayEngine.cfg") -rayEngine.loadWeapons(applicationResourcesDirectory .. "Weapons/") -rayEngine.loadWorld(worldsPath .. "ExampleWorld") -rayEngine.changeResolution(rayEngine.properties.screenResolution.width, rayEngine.properties.screenResolution.height) --- rayEngine.intro() -menu() -rayEngine.update() - -while true do - local e = { event.pull(1) } - if e[1] == "touch" then - if e[5] == 1 then - if not rayEngine.currentWeapon then rayEngine.place(3, 0x3) end - else - if rayEngine.currentWeapon then rayEngine.fire() else rayEngine.destroy(3) end - end - elseif e[1] == "key_down" then - if e[4] > 1 and e[4] < 10 then - rayEngine.changeWeapon(e[4] - 2) - else - if controls[e[1]] and controls[e[1]][e[4]] then controls[e[1]][e[4]]() end - end - elseif e[1] == "key_up" then - if controls[e[1]] and controls[e[1]][e[4]] then controls[e[1]][e[4]]() end - end - rayEngine.update() + +local filesystem = require("Filesystem") +local screen = require("Screen") +local GUI = require("GUI") +local system = require("System") +local event = require("Event") + +---------------------------------------------------------------------------------------------------------------------------------- + +local applicationResourcesDirectory = filesystem.path(system.getCurrentScript()) +local localization = system.getLocalization(applicationResourcesDirectory .. "Localizations/") +local worldsPath = applicationResourcesDirectory .. "Worlds/" +local rayWalkVersion = "RayWalk Tech Demo v3.5" + +local rayEngine = dofile(applicationResourcesDirectory .. "RayEngine.lua") + +---------------------------------------------------------------------------------------------------------------------------------- + +local function menuBackground() + rayEngine.drawWorld() + screen.clear(0x000000, 0.5) +end + +local function settings() + local window = GUI.workspace() + local oldDraw = window.draw + window.draw = function() + menuBackground() + oldDraw(window) + end + + local sliderWidth, textBoxWidth = 43, 19 + local x, y = math.floor(window.width / 2 - sliderWidth / 2), math.floor(window.height / 2 - 19) + + window:addChild(GUI.label(1, y, window.width, 1, 0xFFFFFF, localization.rayEngineProperties)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); y = y + 3 + + local resolutionTextBoxWidth = window:addChild(GUI.input(x, y, textBoxWidth, 3, 0x262626, 0xBBBBBB, 0xBBBBBB, 0x262626, 0xFFFFFF, tostring(screen.getWidth()), nil, true)) + window:addChild(GUI.label(x + textBoxWidth + 2, y + 1, 1, 1, 0xFFFFFF, "X")) + local resolutionTextBoxHeight = window:addChild(GUI.input(x + textBoxWidth + 5, y, textBoxWidth, 3, 0x262626, 0xBBBBBB, 0xBBBBBB, 0x262626, 0xFFFFFF, tostring(screen.getHeight()), nil, true)); y = y + 4 + window:addChild(GUI.label(1, y, window.width, 1, 0xDDDDDD, localization.screenResolution)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); y = y + 3 + resolutionTextBoxWidth.validator = function(text) local num = tonumber(text); if num and num >= 40 and num <= 160 then return true end end + resolutionTextBoxHeight.validator = function(text) local num = tonumber(text); if num and num >= 12 and num <= 50 then return true end end + local function onAnyResolutionTextBoxInputFinished() + window:stop() + rayEngine.changeResolution(tonumber(resolutionTextBoxWidth.text), tonumber(resolutionTextBoxHeight.text)) + settings() + end + resolutionTextBoxWidth.onInputFinished = onAnyResolutionTextBoxInputFinished + resolutionTextBoxHeight.onInputFinished = onAnyResolutionTextBoxInputFinished + + local drawDistanceSlider = window:addChild(GUI.slider(x, y, sliderWidth, 0xFFDB80, 0x000000, 0xFFDB40, 0xDDDDDD, 100, 5000, rayEngine.properties.drawDistance, true, localization.drawDistance)) + drawDistanceSlider.onValueChanged = function() + rayEngine.properties.drawDistance = drawDistanceSlider.value + window:draw() + end; y = y + 4 + + local shadingDistanceSlider = window:addChild(GUI.slider(x, y, sliderWidth, 0xFFDB80, 0x000000, 0xFFDB40, 0xDDDDDD, 100, 3000, rayEngine.properties.shadingDistance, true, localization.shadingDistance)) + shadingDistanceSlider.onValueChanged = function() + rayEngine.properties.shadingDistance = shadingDistanceSlider.value + window:draw() + end; y = y + 4 + + local shadingCascadesSlider = window:addChild(GUI.slider(x, y, sliderWidth, 0xFFDB80, 0x000000, 0xFFDB40, 0xDDDDDD, 2, 48, rayEngine.properties.shadingCascades, true, localization.shadingCascades)) + shadingCascadesSlider.onValueChanged = function() + rayEngine.properties.shadingCascades = shadingCascadesSlider.value + window:draw() + end; y = y + 4 + + local raycastQualitySlider = window:addChild(GUI.slider(x, y, sliderWidth, 0xFFDB80, 0x000000, 0xFFDB40, 0xDDDDDD, 0.5, 32, rayEngine.properties.raycastQuality, true, localization.raycastQuality)) + raycastQualitySlider.onValueChanged = function() + rayEngine.properties.raycastQuality = raycastQualitySlider.value + window:draw() + end; y = y + 4 + + local currentTimeSlider = window:addChild(GUI.slider(x, y, sliderWidth, rayEngine.world.colors.sky.current, 0x000000, rayEngine.world.colors.sky.current, 0xDDDDDD, 0, rayEngine.world.dayNightCycle.length, rayEngine.world.dayNightCycle.currentTime, true, localization.dayNightCycle, localization.seconds)) + currentTimeSlider.onValueChanged = function() + rayEngine.world.dayNightCycle.currentTime = currentTimeSlider.value + rayEngine.refreshTimeDependentColors() + currentTimeSlider.colors.active = rayEngine.world.colors.sky.current + currentTimeSlider.colors.pipe = rayEngine.world.colors.sky.current + window:draw() + end; y = y + 4 + + window:addChild(GUI.label(x, y, sliderWidth, 1, 0xDDDDDD, localization.enableSemipixelRenderer)) + + local graphonSwitch = window:addChild(GUI.switch(x + sliderWidth - 8, y, 8, 0xFFDB40, 0xAAAAAA, 0xFFFFFF, not rayEngine.properties.useSimpleRenderer)) + graphonSwitch.onStateChanged = function() + rayEngine.properties.useSimpleRenderer = not graphonSwitch.state + window:draw() + end; y = y + 3 + + window:addChild(GUI.label(x, y, sliderWidth, 1, 0xDDDDDD, localization.enableDayNightCycle)) + + local lockTimeSwitch = window:addChild(GUI.switch(x + sliderWidth - 8, y, 8, 0xFFDB40, 0xAAAAAA, 0xFFFFFF, rayEngine.world.dayNightCycle.enabled)) + lockTimeSwitch.onStateChanged = function() + rayEngine.world.dayNightCycle.enabled = lockTimeSwitch.state + window:draw() + end; y = y + 3 + + window:addChild(GUI.button(x, y, sliderWidth, 3, 0xEEEEEE, 0x262626, 0xBBBBBB, 0x262626, localization.continue)).onTouch = function() window:stop(); filesystem.writeTable(applicationResourcesDirectory .. "RayEngine.cfg", rayEngine.properties, true) end + + window:draw() + window:start() +end + +local function menu() + local window = GUI.workspace() + local oldDraw = window.draw + window.draw = function() + menuBackground() + oldDraw(window) + end + + local buttonWidth, buttonHeight = 50, 3 + local worlds = filesystem.list(worldsPath) + for i = 1, #worlds do + worlds[i] = unicode.sub(worlds[i], 1, -2) + end + + local x, y = math.floor(window.width / 2 - buttonWidth / 2), math.floor(window.height / 2 - #worlds * (buttonHeight + 1) / 2 - 11) + + window:addChild(GUI.label(1, y, window.width, 1, 0xFFFFFF, rayWalkVersion)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); y = y + 3 + window:addChild(GUI.button(x, y, buttonWidth, buttonHeight, 0xEEEEEE, 0x262626, 0xBBBBBB, 0x262626, localization.continue)).onTouch = function() window:stop() end; y = y + buttonHeight + 1 + window:addChild(GUI.button(x, y, buttonWidth, buttonHeight, 0xEEEEEE, 0x262626, 0xBBBBBB, 0x262626, localization.settings)).onTouch = function() window:stop(); settings() end; y = y + buttonHeight + 1 + window:addChild(GUI.button(x, y, buttonWidth, buttonHeight, 0xEEEEEE, 0x262626, 0x999999, 0x262626, localization.exit)).onTouch = function() screen.clear(0x000000); error("interrupted", 0) end; y = y + buttonHeight + 1 + window:addChild(GUI.label(1, y, window.width, 1, 0xFFFFFF, localization.loadWorld)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); y = y + 2 + + for i = 1, #worlds do + window:addChild(GUI.button(x, y, buttonWidth, buttonHeight, 0xEEEEEE, 0x262626, 0xBBBBBB, 0x262626, worlds[i])).onTouch = function() rayEngine.loadWorld(worldsPath .. worlds[i]); window:stop() end + y = y + buttonHeight + 1 + end + + local lines = {}; for i = 1, #localization.controlsHelp do table.insert(lines, localization.controlsHelp[i]) end + table.insert(lines, 1, " ") + table.insert(lines, 1, {text = localization.controls, color = 0xFFFFFF}) + window:addChild(GUI.textBox(1, y, window.width, #lines, nil, 0xCCCCCC, lines, 1):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)); y = y + #lines + 1 + + window:draw() + window:start() +end + + +---------------------------------------------------------------------------------------------------------------------------------- + +local controls = { + ["key_down"] = { + [16] = rayEngine.turnLeft, --q + [18] = rayEngine.turnRight, --e + [30] = rayEngine.moveLeft, --a + [32] = rayEngine.moveRight, --d + [17] = rayEngine.moveForward, --w + [31] = rayEngine.moveBackward, --s + [50] = rayEngine.toggleMinimap, --m + [37] = rayEngine.toggleCompass, --k + [25] = rayEngine.toggleWatch, --p + [14] = menu, --backspace + [28] = rayEngine.commandLine, --enter + [57] = rayEngine.jump, --space + [29] = rayEngine.crouch, --ctrl + [59] = rayEngine.toggleDebugInformation, -- F1 + }, + ["key_up"] = { + [29] = rayEngine.crouch, --ctrl + }, +} + +-------------------------------------------------------------------------------------------------------------- + +rayEngine.loadEngineProperties(applicationResourcesDirectory .. "RayEngine.cfg") +rayEngine.loadWeapons(applicationResourcesDirectory .. "Weapons/") +rayEngine.loadWorld(worldsPath .. "ExampleWorld") +rayEngine.changeResolution(rayEngine.properties.screenResolution.width, rayEngine.properties.screenResolution.height) +-- rayEngine.intro() +menu() +rayEngine.update() + +while true do + local e = { event.pull(1) } + if e[1] == "touch" then + if e[5] == 1 then + if not rayEngine.currentWeapon then rayEngine.place(3, 0x3) end + else + if rayEngine.currentWeapon then rayEngine.fire() else rayEngine.destroy(3) end + end + elseif e[1] == "key_down" then + if e[4] > 1 and e[4] < 10 then + rayEngine.changeWeapon(e[4] - 2) + else + if controls[e[1]] and controls[e[1]][e[4]] then controls[e[1]][e[4]]() end + end + elseif e[1] == "key_up" then + if controls[e[1]] and controls[e[1]][e[4]] then controls[e[1]][e[4]]() end + end + rayEngine.update() end \ No newline at end of file diff --git a/Applications/RayWalk.app/RayEngine.cfg b/Applications/RayWalk.app/RayEngine.cfg new file mode 100755 index 00000000..0d11d9c0 --- /dev/null +++ b/Applications/RayWalk.app/RayEngine.cfg @@ -0,0 +1,28 @@ +{ + screenResolution = { + height = 50, + width = 160 + }, + shadingTransparencyMap = { + [12] = 0.50196078431373, + [6] = 1, + [13] = 0.4, + [1] = 0.2, + [3] = 0.6, + [7] = 1, + [8] = 0.8, + [4] = 0.8, + [9] = 0.8, + [14] = 0.30196078431373, + [2] = 0.4, + [5] = 1, + [11] = 0.6, + [10] = 0.70196078431373 + }, + shadingDistance = 500, + shadingCascades = 8, + useSimpleRenderer = false, + tileWidth = 32, + raycastQuality = 6.4, + drawDistance = 1000 +} \ No newline at end of file diff --git a/lib/rayEngine.lua b/Applications/RayWalk.app/RayEngine.lua similarity index 87% rename from lib/rayEngine.lua rename to Applications/RayWalk.app/RayEngine.lua index 24511039..9ba89baa 100755 --- a/lib/rayEngine.lua +++ b/Applications/RayWalk.app/RayEngine.lua @@ -1,566 +1,564 @@ - -local component = require("component") -local computer = require("computer") -local advancedLua = require("advancedLua") -local color = require("color") -local image = require("image") -local unicode = require("unicode") -local buffer = require("doubleBuffering") -local GUI = require("GUI") -local event = require("event") - ----------------------------------------------------- Константы ------------------------------------------------------------------ - -local rayEngine = {} - -rayEngine.debugInformationEnabled = true -rayEngine.minimapEnabled = true -rayEngine.compassEnabled = false -rayEngine.watchEnabled = false -rayEngine.drawFieldOfViewOnMinimap = false -rayEngine.chatShowTime = 4 -rayEngine.chatHistory = {} - ----------------------------------------------- Расчетные функции ------------------------------------------------------------------ - --- Позиция горизонта, относительно которой рисуется мир -function rayEngine.calculateHorizonPosition() - rayEngine.horizonPosition = math.floor(buffer.getHeight() / 2) -end - --- Размер панели чата и лимита его истории -function rayEngine.calculateChatSize() - rayEngine.chatPanelWidth, rayEngine.chatPanelHeight = math.floor(buffer.getWidth() * 0.4), math.floor(buffer.getHeight() * 0.4) - rayEngine.chatHistoryLimit = rayEngine.chatPanelHeight -end - --- Шаг, с которым будет изменяться угол рейкаста -function rayEngine.calculateRaycastStep() - rayEngine.raycastStep = rayEngine.player.fieldOfView / buffer.getWidth() -end - --- Позиция оружия на экране и всех его вспомогательных текстур -function rayEngine.calculateWeaponPosition() - rayEngine.currentWeapon.xWeapon = buffer.getWidth() - rayEngine.currentWeapon.weaponTexture[1] + 1 - rayEngine.currentWeapon.yWeapon = buffer.getHeight() - rayEngine.currentWeapon.weaponTexture[2] + 1 - rayEngine.currentWeapon.xFire = rayEngine.currentWeapon.xWeapon + rayEngine.weapons[rayEngine.currentWeapon.ID].firePosition.x - rayEngine.currentWeapon.yFire = rayEngine.currentWeapon.yWeapon + rayEngine.weapons[rayEngine.currentWeapon.ID].firePosition.y - rayEngine.currentWeapon.xCrosshair = math.floor(buffer.getWidth() / 2 - rayEngine.currentWeapon.crosshairTexture[1] / 2) - rayEngine.currentWeapon.yCrosshair = math.floor(buffer.getHeight() / 2 - rayEngine.currentWeapon.crosshairTexture[2] / 2) -end - --- Грубо говоря, это расстояние от камеры до виртуального экрана, на котором рисуется весь наш мир, влияет на размер блоков -function rayEngine.calculateDistanceToProjectionPlane() - rayEngine.distanceToProjectionPlane = (buffer.getWidth() / 2) / math.tan(math.rad((rayEngine.player.fieldOfView / 2))) -end - --- Быстрый перерасчет всего, что нужно -function rayEngine.calculateAllParameters() - rayEngine.calculateHorizonPosition() - rayEngine.calculateChatSize() - rayEngine.calculateRaycastStep() - rayEngine.calculateDistanceToProjectionPlane() - if rayEngine.currentWeapon then rayEngine.calculateWeaponPosition() end -end - ----------------------------------------------- Вспомогательные функции ------------------------------------------------------------------ - -local function constrainAngle(value) - if ( value < 0 ) then - value = value + 360 - elseif ( value > 360 ) then - value = value - 360 - end - return value -end - -local function getSkyColorByTime() - return rayEngine.world.colors.sky[rayEngine.world.dayNightCycle.currentTime > 0 and math.ceil(rayEngine.world.dayNightCycle.currentTime / rayEngine.world.dayNightCycle.length * #rayEngine.world.colors.sky) or 1] -end - -local function getBrightnessByTime() - return rayEngine.properties.shadingTransparencyMap[rayEngine.world.dayNightCycle.currentTime > 0 and math.ceil(rayEngine.world.dayNightCycle.currentTime / rayEngine.world.dayNightCycle.length * #rayEngine.properties.shadingTransparencyMap) or 1] -end - -local function getTileColor(basecolor, distance) - local limitedDistance = math.floor(distance * rayEngine.properties.shadingCascades / rayEngine.properties.shadingDistance) - local transparency = rayEngine.currentShadingTransparencyMapValue - limitedDistance / rayEngine.properties.shadingCascades - transparency = (transparency >= rayEngine.properties.shadingTransparencyMap[1] and transparency <= 1) and transparency or rayEngine.properties.shadingTransparencyMap[1] - return color.blend(basecolor, 0x000000, transparency) -end - -function rayEngine.refreshTimeDependentColors() - rayEngine.world.colors.sky.current = getSkyColorByTime() - rayEngine.currentShadingTransparencyMapValue = getBrightnessByTime() - rayEngine.world.colors.groundByTime = color.blend(rayEngine.world.colors.ground, 0x000000, rayEngine.currentShadingTransparencyMapValue) -end - -local function doDayNightCycle() - if rayEngine.world.dayNightCycle.enabled then - local computerUptime = computer.uptime() - if (computerUptime - rayEngine.world.dayNightCycle.lastComputerUptime) >= rayEngine.world.dayNightCycle.speed then - rayEngine.world.dayNightCycle.currentTime = rayEngine.world.dayNightCycle.currentTime + rayEngine.world.dayNightCycle.speed - if rayEngine.world.dayNightCycle.currentTime > rayEngine.world.dayNightCycle.length then rayEngine.world.dayNightCycle.currentTime = 0 end - rayEngine.world.dayNightCycle.lastComputerUptime = computerUptime - - rayEngine.refreshTimeDependentColors() - end - end -end - -local function convertWorldCoordsToMapCoords(x, y) - return math.round(x / rayEngine.properties.tileWidth), math.round(y / rayEngine.properties.tileWidth) -end - -local function getBlockCoordsByLook(distance) - local radRotation = math.rad(rayEngine.player.rotation) - return convertWorldCoordsToMapCoords(rayEngine.player.position.x + distance * math.sin(radRotation) * rayEngine.properties.tileWidth, rayEngine.player.position.y + distance * math.cos(radRotation) * rayEngine.properties.tileWidth) -end - ----------------------------------------------------- Работа с файлами ------------------------------------------------------------------ - --- Загрузка параметров движка -function rayEngine.loadEngineProperties(pathToRayEnginePropertiesFile) - rayEngine.properties = table.fromFile(pathToRayEnginePropertiesFile) -end - --- Загрузка конифгурации оружия -function rayEngine.loadWeapons(pathToWeaponsFolder) - rayEngine.weaponsFolder = pathToWeaponsFolder - rayEngine.weapons = table.fromFile(rayEngine.weaponsFolder .. "Weapons.cfg") - rayEngine.changeWeapon(1) -end - --- Загрузка конкретного мира -function rayEngine.loadWorld(pathToWorldFolder) - rayEngine.world = table.fromFile(pathToWorldFolder .. "/World.cfg") - rayEngine.map = table.fromFile(pathToWorldFolder .. "/Map.cfg") - rayEngine.player = table.fromFile(pathToWorldFolder .. "/Player.cfg") - rayEngine.blocks = table.fromFile(pathToWorldFolder .. "/Blocks.cfg") - -- Дополняем карту ее размерами - rayEngine.map.width = #rayEngine.map[1] - rayEngine.map.height = #rayEngine.map - -- Ебашим правильную позицию игрока, основанную на этой ХУЙНЕ, которую ГЛЕБ так ЛЮБИТ - rayEngine.player.position.x = rayEngine.properties.tileWidth * rayEngine.player.position.x - rayEngine.properties.tileWidth / 2 - rayEngine.player.position.y = rayEngine.properties.tileWidth * rayEngine.player.position.y - rayEngine.properties.tileWidth / 2 - -- Рассчитываем цвета, зависимые от времени - небо, землю, стены - rayEngine.refreshTimeDependentColors() - -- Обнуляем текущее время, если превышен лимит, а то мало ли какой пидорас начнет править конфиги мира - rayEngine.world.dayNightCycle.currentTime = rayEngine.world.dayNightCycle.currentTime > rayEngine.world.dayNightCycle.length and 0 or rayEngine.world.dayNightCycle.currentTime - -- Осуществляем базовое получение аптайма пекарни - rayEngine.world.dayNightCycle.lastComputerUptime = computer.uptime() - -- Рассчитываем необходимые параметры движка - rayEngine.calculateAllParameters() - - -- rayEngine.wallsTexture = image.load("/heart.pic") - -- rayEngine.wallsTexture = image.transform(rayEngine.wallsTexture, rayEngine.properties.tileWidth, rayEngine.properties.tileWidth / 2) -end - ----------------------------------------------------- Функции, связанные с игроком ------------------------------------------------------------------ - -function rayEngine.changeWeapon(weaponID) - if rayEngine.weapons[weaponID] then - rayEngine.currentWeapon = { - ID = weaponID, - damage = rayEngine.weapons[weaponID].damage, - weaponTexture = image.load(rayEngine.weaponsFolder .. rayEngine.weapons[weaponID].weaponTexture), - fireTexture = image.load(rayEngine.weaponsFolder .. rayEngine.weapons[weaponID].fireTexture), - crosshairTexture = image.load(rayEngine.weaponsFolder .. rayEngine.weapons[weaponID].crosshairTexture) - } - rayEngine.calculateWeaponPosition() - else - rayEngine.currentWeapon = nil - end -end - -function rayEngine.move(distanceForward, distanceRight) - local forwardRotation = math.rad(rayEngine.player.rotation) - local rightRotation = math.rad(rayEngine.player.rotation + 90) - local xNew = rayEngine.player.position.x + distanceForward * math.sin(forwardRotation) + distanceRight * math.sin(rightRotation) - local yNew = rayEngine.player.position.y + distanceForward * math.cos(forwardRotation) + distanceRight * math.cos(rightRotation) - - local xWorld, yWorld = convertWorldCoordsToMapCoords(xNew, yNew) - if rayEngine.map[yWorld][xWorld] == nil then - rayEngine.player.position.x, rayEngine.player.position.y = xNew, yNew - end -end - -function rayEngine.rotate(angle) - rayEngine.player.rotation = constrainAngle(rayEngine.player.rotation + angle) -end - -function rayEngine.turnRight() - rayEngine.rotate(rayEngine.player.rotationSpeed) -end - -function rayEngine.turnLeft() - rayEngine.rotate(-rayEngine.player.rotationSpeed) -end - -function rayEngine.moveForward() - rayEngine.move(rayEngine.player.moveSpeed, 0) -end - -function rayEngine.moveBackward() - rayEngine.move(-rayEngine.player.moveSpeed, 0) -end - -function rayEngine.moveLeft() - rayEngine.move(0, -rayEngine.player.moveSpeed) -end - -function rayEngine.moveRight() - rayEngine.move(0, rayEngine.player.moveSpeed) -end - -function rayEngine.jump() - if not rayEngine.player.jumpTimer then - local function onJumpFinished() - rayEngine.horizonPosition = rayEngine.horizonPosition - rayEngine.player.jumpHeight; - rayEngine.horizonPosition = rayEngine.horizonPosition - rayEngine.player.jumpHeight; - rayEngine.player.jumpTimer = nil - end - - rayEngine.player.jumpTimer = event.timer(1, onJumpFinished) - rayEngine.horizonPosition = rayEngine.horizonPosition + rayEngine.player.jumpHeight - rayEngine.horizonPosition = rayEngine.horizonPosition + rayEngine.player.jumpHeight - end -end - -function rayEngine.crouch() - rayEngine.player.isCrouched = not rayEngine.player.isCrouched - local heightAdder = rayEngine.player.isCrouched and -rayEngine.player.crouchHeight or rayEngine.player.crouchHeight - rayEngine.horizonPosition = rayEngine.horizonPosition + heightAdder - rayEngine.horizonPosition = rayEngine.horizonPosition + heightAdder -end - -function rayEngine.destroy(distance) - local xBlock, yBlock = getBlockCoordsByLook(distance) - if rayEngine.map[yBlock] and rayEngine.map[yBlock][xBlock] and rayEngine.blocks[rayEngine.map[yBlock][xBlock]] and rayEngine.blocks[rayEngine.map[yBlock][xBlock]].canBeDestroyed then rayEngine.map[yBlock][xBlock] = nil end -end - -function rayEngine.place(distance, blockColor) - local xBlock, yBlock = getBlockCoordsByLook(distance) - if rayEngine.map[yBlock] and rayEngine.map[yBlock][xBlock] == nil then rayEngine.map[yBlock][xBlock] = blockColor end -end - ----------------------------------------------------- Функции интерфейса ------------------------------------------------------------------ - -function rayEngine.drawDebugInformation(x, y, width, transparency, ...) - local lines = {...} - buffer.drawRectangle(x, y, width, #lines, 0x000000, 0x000000, " ", transparency); x = x + 1 - for line = 1, #lines do buffer.drawText(x, y, 0xEEEEEE, lines[line]); y = y + 1 end -end - -local function drawFieldOfViewAngle(x, y, distance, color) - local fieldOfViewHalf = rayEngine.player.fieldOfView / 2 - local firstAngle, secondAngle = math.rad(-(rayEngine.player.rotation - fieldOfViewHalf)), math.rad(-(rayEngine.player.rotation + fieldOfViewHalf)) - local xFirst, yFirst = math.floor(x + math.sin(firstAngle) * distance), math.floor(y + math.cos(firstAngle) * distance) - local xSecond, ySecond = math.floor(x + math.sin(secondAngle) * distance), math.floor(y + math.cos(secondAngle) * distance) - buffer.drawSemiPixelLine(x, y, xFirst, yFirst, color) - buffer.drawSemiPixelLine(x, y, xSecond, ySecond, color) -end - -function rayEngine.drawMap(x, y, width, height, transparency) - local xHalf, yHalf = math.floor(width / 2), math.floor(height / 2) - local xMap, yMap = convertWorldCoordsToMapCoords(rayEngine.player.position.x, rayEngine.player.position.y) - - buffer.drawRectangle(x, y, width, yHalf, 0x000000, 0x000000, " ", transparency) - - local xPos, yPos = x, y * 2 - 1 - for i = yMap - yHalf + 1, yMap + yHalf do - for j = xMap + xHalf + 1, xMap - xHalf + 2, -1 do - if rayEngine.map[i] and rayEngine.map[i][j] then - buffer.semiPixelSet(xPos, yPos, rayEngine.blocks[rayEngine.map[i][j]].color) - end - xPos = xPos + 1 - end - xPos = x; yPos = yPos + 1 - end - - local xPlayer, yPlayer = x + xHalf, y + yHalf - --Поле зрения - if rayEngine.drawFieldOfViewOnMinimap then drawFieldOfViewAngle(xPlayer, yPlayer, 5, 0xCCFFBF) end - --Игрок - buffer.semiPixelSet(xPlayer, yPlayer, 0x66FF40) -end - -function rayEngine.intro() - local logo = image.fromString("17060000FF 0000FF 0000FF 0000FF 007EFF▄007EFF▄007EFF▄007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀007EFF▄007EFF▄007EFF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 007EFF▄007EFF▀007EFF▀0000FF 0000FF 0000FF 0000FF 0053FF▄0053FF▀0053FF▀0053FF▀0053FF▄0000FF 0000FF 0000FF 0000FF 007EFF▀007EFF▀007EFF▄0000FF 0000FF 0000FF 007EFF▀007EFF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 530000 0000FF 0078FF▀0000FF 537800▀0078FF▀0078FF▀0078FF▀0078FF▀0078FF▀0078FF▀7E7800▀0078FF▀0000FF 0078FF▀0000FF 0000FF 007EFF▀007EFF▀007EFF▄007EFF▄007EFF▄0000FF 0000FF 0053FF▀0053FF▀0053FF▀0000FF 0000FF 007EFF▄007EFF▄007EFF▄007EFF▀007EFF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 007EFFP007EFFo007EFFw007EFFe007EFFr007EFFe007EFFd0000FF 007EFFb007EFFy0000FF 007EFFR007EFFa007EFFy007EFFE007EFFn007EFFg007EFFi007EFFn007EFFe007EFF™0000FF 0000FF ") - local x, y = math.floor(buffer.getWidth() / 2 - logo[1] / 2), math.floor(buffer.getHeight() / 2 - logo[2] / 2) - local function draw(transparency) - buffer.clear(0xF0F0F0); - buffer.drawImage(x, y, logo) - buffer.drawRectangle(1, 1, buffer.getWidth(), buffer.getHeight(), 0x000000, 0x000000, " ", transparency) - buffer.drawChanges() - os.sleep(0) - end - for i = 0, 100, 20 do draw(i) end - os.sleep(1.5) - for i = 100, 0, -20 do draw(i) end -end - -function rayEngine.compass(x, y) - if not rayEngine.compassImage then rayEngine.compassImage = image.fromString("1C190000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 553600▄373100▄543600▄373600▄543600▄373100▄540000 375400▄673700▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0055FF▄675400▄553700▄375500▄550000 375500▄540000 375400▄540000 373600▄310000 675500▄677E00▄375300▄365400▄373600▄540000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 555400▄553700▄540000 543700▄540000 375300▄533100▄310B00▄310000 310000 360000 543100▄375300▄553100▄533600▄543200▄313600▄372A00▄373100▄0054FF▄0054FF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 540000 543200▄540000 545300▄540600▄063100▄310000 315400▄365400▄373100▄313600▄530000 0000FF 0000FF 535400▄535400▄540000 365300▄533100▄065300▄310000 530600▄0054FF▄0000FF 0000FF 0000FF 0000FF 0000FF 530000 365300▄543600▄310600▄312A00▄2A5300▄365300▄543600▄540000 365300▄315300▄530000 0000FF 0000FF 535400▄540000 540000 313600▄363100▄540000 313600▄315300▄062A00▄543100▄0000FF 0000FF 0000FF 0000FF 315300▄533600▄312A00▄2A3100▄315300▄533600▄315400▄540000 535400▄540000 530000 533100▄0000FF 0000FF 540000 540000 315400▄540000 543100▄530000 543100▄313600▄2A0000 2A2900▄540000 0000FF 0000FF 0000FF 533100▄315400▄530000 062A00▄533100▄315300▄535400▄533100▄540000 315400▄543100▄530000 0000FF 0000FF 540000 535400▄315300▄535400▄363100▄535400▄530000 530000 312A00▄2A5300▄0054FF▀0000FF 0000FF 0000FF 312A00▄530000 553600▄2A5500▄2A0000 312A00▄533600▄545300▄315400▄535400▄540000 540000 0053FF▄0053FF▄540000 530000 535400▄535400▄545300▄530000 363100▄312A00▄313600▄545300▄0000FF 0000FF 0000FF 0000FF 530000 535400▄540000 545300▄555400▄315500▄315400▄533100▄543100▄530000 533100▄530000 535400▄535500▄533100▄535400▄315300▄533100▄533600▄315300▄530000 542A00▄312800▄0029FF▀0000FF 0000FF 0000FF 0000FF 530000 545500▄540000 555300▄535400▄530000 542A00▄545300▄365400▄530000 543600▄2A5400▄547E00▄550000 545300▄533600▄540000 530000 530000 542A00▄2A0000▄0029FF▀0000FF 0000FF 0000FF 0000FF 0000FF 530000 535400▄540000 540000 540000 545300▄530000 540000 2A5500▄545300▄292A00▄290000 292A00▄290000 295400▄292A00▄292A00▄290000 542A00▄543600▄285400▄0054FF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 530000 533100▄540000 530000 535400▄0053FF▀0053FF▀542800▄542800▄532800▄2A2900▄542900▄532900▄542900▄542900▄532900▄542800▄2A2900▄2A2800▄532900▄552800▄542800▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 2A5300▄530000 535400▄540000 530000 532A00▀2A5300▄2A5500▄0029FF▀0028FF▀0028FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0028FF▀0028FF▀0028FF▀295300▄535500▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 535400▄545300▄530000 545300▄530000 2A0000 2A5300▄545300▄532A00▄542900▄532900▄2A2900▄2A2900▄2A2800▄2A2800▄2A2800▄2A2900▄2A2900▄532900▄532900▄2A2900▄542A00▄545300▄0054FF▄0054FF▄0000FF 0000FF 0000FF 545300▄530000 530000 545300▄2A5300▄532A00▄542900▄290000 295400▄297F00▄548100▄548100▄558100▄558100▄558100▄558100▄538100▄2A8100▄007E00▄295400▄2A2900▄295400▄2A2900▄290000 542A00▄557E00▄0000FF 0000FF 530000 2A0000 545300▄530000 532900▄285400▄2A8000▄7F8100▄810000 810000 810000 810000 810000 810000 812A00N810000 810000 810000 810000 810000 808100▄548100▄545500▄295300▄282900▄542900▄0000FF 0000FF 2A0000 2A0000 545300▄2A2900▄298000▄810000 810000 815500381550018155005810000 810000 810000 810000 810000 810000 810000 810000 81550048155005810000 810000 810000 558100▄2A5300▄282900▄0029FF▄0000FF 532A00▄2A0000 545300▄547E00▄810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 7F8000▄290000 292800▄0000FF 2A0000 2A0000 2A5300▄7E5300▄810000 812A00W810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 812A00E810000 807E00▄2A2900▄292800▄0000FF 2A0000 2A2900▄2A0000 2A0000 552A00▄810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 815500▄542900▄280000 0028FF▀530000 2A0000 2A0000 290000 2A2900▄545300▄552A00▄815500▄810000 815500281550028155005810000 810000 810000 810000 810000 810000 810000 815500181550038155005817F00▄552900▄292800▄280000 282A00▄0000FF 530000 2A0000 540000 2A5300▄282A00▄290000 532900▄542A00▄542800▄552900▄7F5300▄815500▄817F00▄817F00▄810000 812A00S810000 817F00▄815500▄815500▄555400▄532900▄292800▄280000 282A00▄282A00▄530000 0000FF 532A00▄2A0000 530000 530000 2A5300▄290000 282900▄002900▄280000 280000 280000 290000 292A00▄2A2900▄542900▄532900▄2A0000 295300▄282900▄280000 280000 280000 282900▄295300▄295300▄2A5300▄552A00▄0000FF 530000 295300▄535400▄540000 535400▄540000 535400▄2A5500▄282900▄280000▄280000 280000▄290000▄2A2800▄2A2900▄552A00▄7E0000▄2A0000▄280000▄280000 280000▄280000▄002AFF▀002AFF▀002AFF▀002AFF▀0000FF 0000FF 532900▄532800▄0029FF▀0029FF▀0029FF▀0029FF▀0029FF▀0029FF▀0000FF 2A9800▄285500▄547E00▄7E5400▄7F5300▄7E2900▄7E2900▄552A00▄542A00▄2A5300▄282A00▄2A7E00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF ") end - buffer.drawImage(x, y, rayEngine.compassImage) - - x, y = x + 15, y + 17 - local distance = 3.4 - local northAngle = -rayEngine.player.rotation - local xScaleFactor = 2.2 - local southPoint, northPoint = {}, {} - local northAngleRad = math.rad(northAngle) - northPoint.x, northPoint.y = math.round(x + math.sin(northAngleRad) * distance * xScaleFactor), math.round(y - math.cos(northAngleRad) * distance) - northAngleRad = math.rad(northAngle + 180) - southPoint.x, southPoint.y = math.round(x + math.sin(northAngleRad) * distance * xScaleFactor), math.round(y - math.cos(northAngleRad) * distance) - - y = y * 2 - buffer.drawSemiPixelLine(x, y, northPoint.x, northPoint.y * 2, 0xFF5555) - buffer.drawSemiPixelLine(x, y, southPoint.x, southPoint.y * 2, 0xFFFFFF) - buffer.semiPixelSet(x, y, 0x000000) -end - -function rayEngine.watch(x, y) - if not rayEngine.watchImage then rayEngine.watchImage = image.fromString("20190000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0053FF▄552900▄673100▄7E2A00▄7E3100▄7E2A00▄7E3100▄672A00▄7E2A00▄672900▄7F3100▄7E2A00▄0053FF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 313600▄290000 290600▄062900▄290000 062900▄290000 062900▄290000 290600▄062900▄2A2900▄2A0600▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 313600▄012900▄2A2900▄290000 012900▄290000 290000 012A00▄290000 290000 312900▄293100▄310600▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 310000 292800▄062A00▄290000 290100▄062900▄290000 290000 2C2900▄290600▄293100▄2A2900▄292800▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0054FF▄557F00▄012800▄290000 290000 292800▄290000 012900▄292800▄290600▄290000 292800▄290000 012800▄807E00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF AB8100▄7F8000▄283100▄280000 015400▄318100▄005300▄065500▄315500▄282A00▄296700▄015500▄290000 002900▄677E00▄81AA00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 807F00▄7F8000▄677E00▄812900▄672800▄542800▄2A0000▄2A0000▄815400▄ACAA00▄555400▄283100▄2A0000 312900▄672800▄7F5300▄7F0000 7F0000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 007EFF▄817E00▄7E2900▄2A0000▄805300▄54AA00▄003100▄2A0000 310600▄532900▄312800▄532900▄360100▄552900▄672800▄7E2A00▄315500▄678000▄555300▄540000▄805400▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0081FF▄802900▄280100▄280000 285400▄310600▄532900▄290000 290000 290000 290000 29D700129D7002290000 290000 290000 282900▄062900▄2A2900▄550600▄556700▄285500▄542800▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0080FF▄557E00▄28AA00▄005500▄552900▄290000 290000 290000 29D700129D7001290000 290000 290000 290000 290000 290000 290000 29D7001290000 290000 290000 290000 672900▄318000▄297F00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 543100▄002800▄553100▄7E2800▄290000 29D700129D7000290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 29D7002290000 290000 7E2900▄7E6700▄282900▄808100▄0000FF 0000FF 0000FF 0000FF 805500▄280000▄290000 310600▄290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 555400▄535400▄533100▄D58000▄0080FF▄0000FF 552900▀550000 54AB00▄558100▄290000 290000 29D7009290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 29D7003290000 315400▄548100▄067E00▄557F00▄806700▄ACAB00▄0055FF▀545500▄AB3100▄815400▄290100▄290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 545300▄AB5500▄815300▄558000▄558000▄0000FF 0000FF 538100▄002800▄290600▄290000 290000 290000 29D7008290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 29D7004290000 290000 530000 000000 286700▄0080FF▀0000FF 0000FF 0000FF 0000FF 292A00▄000100▄530000 285400▄290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 296700▄063100▄280000 818000▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 550000 810000▄532800▄015300▄280000 292800▄290000 29D7007290000 290000 290000 290000 290000 290000 290000 290000 290000 29D7005290000 290100▄292A00▄065300▄7F0000▄802900▄81C900▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 549900▄002900▄292800▄312800▄290600▄283100▄290600▄292800▄290000 290000 290000 29D7006290000 290000 292800▄292800▄290000 285300▄2A0600▄362800▄280000 285500▄0081FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 7EAA00▄317E00▄005300▄547E00▄7F2900▄290000▄292A00▄063100▄283100▄295500▄286200▄285500▄012A00▄292A00▄310600▄540000▄807E00▄005500▄015500▄530000 0081FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 7E8100▄7E8000▄557F00▄317E00▄2A3600▄283100▄005400▄2A5300▄817E00▄AA7E00▄545300▄005300▄005400▄283100▄537E00▄548100▄7F0000 7E8000▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 819800▄807F00▄280000 002900▄312900▄7E2800▄292800▄532800▄552900▄280000 550100▄542800▄282900▄000100▄7E0000 AA9800▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 555300▄012800▄290000 062900▄290000 062900▄290000 062900▄290000 290000 290000 290600▄280000 007EFF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 313600▄062900▄312900▄290000 012900▄293100▄290000 062900▄312900▄290600▄312900▄2A0000 2A2900▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 315400▄290600▄310000 062900▄290000 293100▄062900▄290000 293100▄290000 293100▄062900▄312A00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0054FF▀285500▄285500▄015500▄285500▄285500▄015500▄295500▄015500▄285500▄015500▄285500▄065500▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF ") end - - buffer.drawImage(x, y, rayEngine.watchImage) - x, y = x + 15, y + 12 - - local realTimeInSeconds = rayEngine.world.dayNightCycle.currentTime * 86400 / rayEngine.world.dayNightCycle.length - local hours = realTimeInSeconds / 3600 - local _, minutes = math.modf(hours) - local hourAngle = math.rad(hours * 360 / 12) - local minuteAngle = math.rad(minutes * 360) - local hourArrowLength, minuteArrowLength = 2.8, 4.5 - local xMinute, yMinute = math.round(x + math.sin(minuteAngle) * minuteArrowLength * 2), math.round(y - math.cos(minuteAngle) * minuteArrowLength) - local xHour, yHour = math.round(x + math.sin(hourAngle) * hourArrowLength * 2), math.round(y - math.cos(hourAngle) * hourArrowLength) - - y = y * 2 - buffer.drawSemiPixelLine(x, y, xMinute, yMinute * 2, 0xEEEEEE) - buffer.drawSemiPixelLine(x, y, xHour, yHour * 2, 0xEEEEEE) -end - -local function addItemToChatHistory(text, color) - text = string.wrap({text}, rayEngine.chatPanelWidth - 2) - table.insert(rayEngine.chatHistory, {color = color, text = text}) - if #rayEngine.chatHistory > rayEngine.chatHistoryLimit then table.remove(rayEngine.chatHistory, 1) end -end - -function rayEngine.chat(transparency) - local x, y = 1, buffer.getHeight() - rayEngine.chatPanelHeight - 3 - buffer.drawRectangle(x, y, rayEngine.chatPanelWidth, rayEngine.chatPanelHeight, 0x000000, 0xFFFFFF, " ", transparency or 0.5) - buffer.setDrawLimit(x, y, x + rayEngine.chatPanelWidth - 1, y + rayEngine.chatPanelHeight - 1) - local yMessage = y + rayEngine.chatPanelHeight - 1 - x = x + 1 - - for message = #rayEngine.chatHistory, 1, -1 do - for line = #rayEngine.chatHistory[message].text, 1, -1 do - buffer.drawText(x, yMessage, rayEngine.chatHistory[message].color or 0xFFFFFF, rayEngine.chatHistory[message].text[line]) - yMessage = yMessage - 1 - if yMessage < y then buffer.resetDrawLimit(); return end - end - end - - buffer.resetDrawLimit() -end - -function rayEngine.commandLine(transparency) - transparency = transparency or 50 - local inputPanelHeight = 3 - local x, y = 1, buffer.getHeight() - inputPanelHeight + 1 - --Врубаем чат и рисуем все, включая его - rayEngine.chatEnabled = true - rayEngine.update() - - --Ввод данных - local input = GUI.input(x, y, buffer.getWidth(), 3, 0xFFFFFF, 0x3C3C3C, 0x666666, 0xFFFFFF, 0x3C3C3C, "") - input.eventHandler({draw = function() input:draw() end}, input, "touch", input.x, input.y) - - local words = {}; for word in string.gmatch(input.text, "[^%s]+") do table.insert(words, unicode.lower(word)) end - if #words > 0 then - if unicode.sub(words[1], 1, 1) == "/" then - words[1] = unicode.sub(words[1], 2, -1) - if words[1] == "time" then - if words[2] == "set" and words[3] and tonumber(words[3]) then - local newTime = tonumber(words[3]) - if newTime < 0 or newTime > rayEngine.world.dayNightCycle.length then - addItemToChatHistory("Время не может быть отрицательным и превышать длину суток (" .. rayEngine.world.dayNightCycle.length .. " секю)", 0xFF8888) - else - rayEngine.world.dayNightCycle.currentTime = math.floor(newTime) - addItemToChatHistory("Время успешно изменено на: " .. newTime, 0xFFDB40) - end - elseif words[2] == "get" then - addItemToChatHistory("Текущее время: " .. rayEngine.world.dayNightCycle.currentTime, 0xFFDB40) - addItemToChatHistory("Длина суток: " .. rayEngine.world.dayNightCycle.length, 0xFFDB40) - elseif words[2] == "lock" then - rayEngine.world.dayNightCycle.enabled = not rayEngine.world.dayNightCycle.enabled - addItemToChatHistory("Состояние цикла дня и ночи: " .. tostring(rayEngine.world.dayNightCycle.enabled), 0xFFDB40) - end - elseif words[1] == "setrenderquality" and tonumber(words[2]) then - rayEngine.properties.raycastQuality = tonumber(words[2]) - addItemToChatHistory("Качество рендера изменено на: " .. tonumber(words[2]), 0xFFDB40) - elseif words[1] == "setdrawdistance" and tonumber(words[2]) then - rayEngine.properties.drawDistance = tonumber(words[2]) - addItemToChatHistory("Дистанция прорисовки изменена на: " .. tonumber(words[2]), 0xFFDB40) - elseif words[1] == "setshadingcascades" and tonumber(words[2]) then - rayEngine.properties.shadingCascades = tonumber(words[2]) - addItemToChatHistory("Количество цветов для отрисовки блока изменено на: " .. tonumber(words[2]), 0xFFDB40) - elseif words[1] == "setshadingdistance" and tonumber(words[2]) then - rayEngine.properties.shadingDistance = tonumber(words[2]) - addItemToChatHistory("Дистация затенения блоков изменена на: " .. tonumber(words[2]), 0xFFDB40) - elseif words[1] == "help" then - addItemToChatHistory("Доступные команды:", 0xFFDB40) - addItemToChatHistory("/time get", 0xFFFFBF) - addItemToChatHistory("/time set ", 0xFFFFBF) - addItemToChatHistory("/time lock", 0xFFFFBF) - addItemToChatHistory(" ", 0xFFFFFF) - addItemToChatHistory("/setRenderQuality ", 0xFFFFBF) - addItemToChatHistory("/setDrawDistance ", 0xFFFFBF) - addItemToChatHistory("/setShadingCascades ", 0xFFFFBF) - addItemToChatHistory("/setShadingDistance ", 0xFFFFBF) - else - addItemToChatHistory("Неизвестная команда. Введите /help для получения списка команд", 0xFF8888) - end - else - addItemToChatHistory("> " .. input.text, 0xFFFFFF) - end - end - - --Активируем таймер - if rayEngine.chatTimer then event.cancel(rayEngine.chatTimer) end - rayEngine.chatEnabled = true - rayEngine.chatTimer = event.timer(rayEngine.chatShowTime, function() rayEngine.chatEnabled = false; rayEngine.chatTimer = nil; update() end) -end - -function rayEngine.toggleMinimap() - rayEngine.minimapEnabled = not rayEngine.minimapEnabled -end - -function rayEngine.toggleDebugInformation() - rayEngine.debugInformationEnabled = not rayEngine.debugInformationEnabled -end - -function rayEngine.toggleCompass() - rayEngine.compassEnabled = not rayEngine.compassEnabled - if not rayEngine.compassEnabled then rayEngine.compassImage = nil end -end - -function rayEngine.toggleWatch() - rayEngine.watchEnabled = not rayEngine.watchEnabled - if not rayEngine.watchEnabled then rayEngine.watchImage = nil end -end - -function rayEngine.drawWeapon() - if rayEngine.currentWeapon.needToFire then buffer.drawImage(rayEngine.currentWeapon.xFire, rayEngine.currentWeapon.yFire, rayEngine.currentWeapon.fireTexture); rayEngine.currentWeapon.needToFire = false end - buffer.drawImage(rayEngine.currentWeapon.xWeapon, rayEngine.currentWeapon.yWeapon, rayEngine.currentWeapon.weaponTexture) - buffer.drawImage(rayEngine.currentWeapon.xCrosshair, rayEngine.currentWeapon.yCrosshair, rayEngine.currentWeapon.crosshairTexture) -end - -function rayEngine.drawStats() - local width = math.floor(buffer.getWidth() * 0.3) - local height = 5 - local x, y = buffer.getWidth() - width - 1, 2 - buffer.drawRectangle(x, y, width, height, 0x000000, 0xFFFFFF, " ", 0.5) - - GUI.progressBar(x + 1, y + 4, width - 2, 1, 0x000000, 0xFF5555, rayEngine.player.health.current, rayEngine.player.health.maximum, true) -end - ----------------------------------------------------- Функции отрисовки мира ------------------------------------------------------------------ - -local function raycast(angle) - angle = math.rad(angle) - local angleSinDistance, angleCosDistance, currentDistance, xWorld, yWorld, xMap, yMap, tile = math.sin(angle) * rayEngine.properties.raycastQuality, math.cos(angle) * rayEngine.properties.raycastQuality, 0, rayEngine.player.position.x, rayEngine.player.position.y - - while true do - if currentDistance <= rayEngine.properties.drawDistance then - xMap, yMap = math.floor(xWorld / rayEngine.properties.tileWidth), math.floor(yWorld / rayEngine.properties.tileWidth) - if rayEngine.map[yMap] and rayEngine.map[yMap][xMap] then - return currentDistance, rayEngine.map[yMap][xMap] - end - - xWorld, yWorld = xWorld + angleSinDistance, yWorld + angleCosDistance - currentDistance = currentDistance + rayEngine.properties.raycastQuality - else - return nil - end - end -end - -function rayEngine.drawWorld() - --Земля - buffer.clear(rayEngine.world.colors.groundByTime) - --Небо - buffer.drawRectangle(1, 1, buffer.getWidth(), rayEngine.horizonPosition, rayEngine.world.colors.sky.current, 0x0, " ") - --Сцена - local startAngle, endAngle, startX, distanceToTile, tileID, height, startY, tileColor = rayEngine.player.rotation - rayEngine.player.fieldOfView / 2, rayEngine.player.rotation + rayEngine.player.fieldOfView / 2, 1 - for angle = startAngle, endAngle, rayEngine.raycastStep do - distanceToTile, tileID = raycast(angle) - if distanceToTile then - -- Получаем цвет стенки - tileColor = getTileColor(rayEngine.blocks[tileID].color, distanceToTile) - - -- Поддержка "высококачественной" doubleHeight-графики - if rayEngine.properties.useSimpleRenderer then - height = rayEngine.properties.tileWidth / distanceToTile * rayEngine.distanceToProjectionPlane - startY = rayEngine.horizonPosition - height / 2 + 1 - buffer.drawRectangle(math.floor(startX), math.floor(startY), 1, math.floor(height), tileColor, 0x000000, " ") - else - height = rayEngine.properties.tileWidth / distanceToTile * rayEngine.distanceToProjectionPlane * 2 - startY = rayEngine.horizonPosition * 2 - height / 2 + 1 - buffer.drawSemiPixelRectangle(math.floor(startX), math.floor(startY), 1, height, tileColor) - end - - --ТИКСТУРКА)))00 - -- local xTexture = startX % rayEngine.properties.tileWidth + 1 - -- if xTexture >= 1 and xTexture <= buffer.getWidth() then - -- local column = image.getColumn(rayEngine.wallsTexture, xTexture) - -- column = image.transform(column, 1, height) - -- buffer.drawImage(math.floor(startX), math.floor(startY), column) - -- end - end - startX = startX + 1 - end -end - -function rayEngine.update() - local frameRenderClock = os.clock() - - rayEngine.drawWorld() - if rayEngine.currentWeapon then rayEngine.drawWeapon() end - if rayEngine.minimapEnabled then rayEngine.drawMap(3, 2, 24, 24, 0.5) end - -- rayEngine.drawStats() - local xTools, yTools = 3, buffer.getHeight() - 25 - if rayEngine.compassEnabled then rayEngine.compass(xTools, yTools); xTools = xTools + 30 end - if rayEngine.watchEnabled then rayEngine.watch(xTools, yTools) end - if rayEngine.chatEnabled then rayEngine.chat() end - doDayNightCycle() - - if rayEngine.debugInformationEnabled then - rayEngine.drawDebugInformation(3, 2 + (rayEngine.minimapEnabled and 12 or 0), 24, 0.6, - "renderTime: " .. string.format("%.2f", (os.clock() - frameRenderClock) * 1000) .. " ms", - "freeRAM: " .. string.format("%.2f", computer.freeMemory() / 1024) .. " KB", - "pos: " .. string.format("%.2f", rayEngine.player.position.x) .. " x " .. string.format("%.2f", rayEngine.player.position.y) - ) - end - - buffer.drawChanges() -end - ----------------------------------------------------------------------------------------------------------------------------------- - -function rayEngine.changeResolution(width, height) - component.gpu.setResolution(width, height) - buffer.flush() - rayEngine.calculateAllParameters() -end - -function rayEngine.fire() - rayEngine.currentWeapon.needToFire = true - rayEngine.update() - os.sleep(0.1) - rayEngine.update() -end - ----------------------------------------------------------------------------------------------------------------------------------- - -return rayEngine + +local text = require("Text") +local number = require("Number") +local color = require("Color") +local image = require("Image") +local screen = require("Screen") +local GUI = require("GUI") +local event = require("Event") +local filesystem = require("Filesystem") + +---------------------------------------------------- Константы ------------------------------------------------------------------ + +local rayEngine = {} + +rayEngine.debugInformationEnabled = true +rayEngine.minimapEnabled = true +rayEngine.compassEnabled = false +rayEngine.watchEnabled = false +rayEngine.drawFieldOfViewOnMinimap = false +rayEngine.chatShowTime = 4 +rayEngine.chatHistory = {} + +---------------------------------------------- Расчетные функции ------------------------------------------------------------------ + +-- Позиция горизонта, относительно которой рисуется мир +function rayEngine.calculateHorizonPosition() + rayEngine.horizonPosition = math.floor(screen.getHeight() / 2) +end + +-- Размер панели чата и лимита его истории +function rayEngine.calculateChatSize() + rayEngine.chatPanelWidth, rayEngine.chatPanelHeight = math.floor(screen.getWidth() * 0.4), math.floor(screen.getHeight() * 0.4) + rayEngine.chatHistoryLimit = rayEngine.chatPanelHeight +end + +-- Шаг, с которым будет изменяться угол рейкаста +function rayEngine.calculateRaycastStep() + rayEngine.raycastStep = rayEngine.player.fieldOfView / screen.getWidth() +end + +-- Позиция оружия на экране и всех его вспомогательных текстур +function rayEngine.calculateWeaponPosition() + rayEngine.currentWeapon.xWeapon = screen.getWidth() - rayEngine.currentWeapon.weaponTexture[1] + 1 + rayEngine.currentWeapon.yWeapon = screen.getHeight() - rayEngine.currentWeapon.weaponTexture[2] + 1 + rayEngine.currentWeapon.xFire = rayEngine.currentWeapon.xWeapon + rayEngine.weapons[rayEngine.currentWeapon.ID].firePosition.x + rayEngine.currentWeapon.yFire = rayEngine.currentWeapon.yWeapon + rayEngine.weapons[rayEngine.currentWeapon.ID].firePosition.y + rayEngine.currentWeapon.xCrosshair = math.floor(screen.getWidth() / 2 - rayEngine.currentWeapon.crosshairTexture[1] / 2) + rayEngine.currentWeapon.yCrosshair = math.floor(screen.getHeight() / 2 - rayEngine.currentWeapon.crosshairTexture[2] / 2) +end + +-- Грубо говоря, это расстояние от камеры до виртуального экрана, на котором рисуется весь наш мир, влияет на размер блоков +function rayEngine.calculateDistanceToProjectionPlane() + rayEngine.distanceToProjectionPlane = (screen.getWidth() / 2) / math.tan(math.rad((rayEngine.player.fieldOfView / 2))) +end + +-- Быстрый перерасчет всего, что нужно +function rayEngine.calculateAllParameters() + rayEngine.calculateHorizonPosition() + rayEngine.calculateChatSize() + rayEngine.calculateRaycastStep() + rayEngine.calculateDistanceToProjectionPlane() + if rayEngine.currentWeapon then rayEngine.calculateWeaponPosition() end +end + +---------------------------------------------- Вспомогательные функции ------------------------------------------------------------------ + +local function constrainAngle(value) + if ( value < 0 ) then + value = value + 360 + elseif ( value > 360 ) then + value = value - 360 + end + return value +end + +local function getSkyColorByTime() + return rayEngine.world.colors.sky[rayEngine.world.dayNightCycle.currentTime > 0 and math.ceil(rayEngine.world.dayNightCycle.currentTime / rayEngine.world.dayNightCycle.length * #rayEngine.world.colors.sky) or 1] +end + +local function getBrightnessByTime() + return rayEngine.properties.shadingTransparencyMap[rayEngine.world.dayNightCycle.currentTime > 0 and math.ceil(rayEngine.world.dayNightCycle.currentTime / rayEngine.world.dayNightCycle.length * #rayEngine.properties.shadingTransparencyMap) or 1] +end + +local function getTileColor(basecolor, distance) + local limitedDistance = math.floor(distance * rayEngine.properties.shadingCascades / rayEngine.properties.shadingDistance) + local transparency = rayEngine.currentShadingTransparencyMapValue - limitedDistance / rayEngine.properties.shadingCascades + transparency = (transparency >= rayEngine.properties.shadingTransparencyMap[1] and transparency <= 1) and transparency or rayEngine.properties.shadingTransparencyMap[1] + return color.blend(basecolor, 0x000000, transparency) +end + +function rayEngine.refreshTimeDependentColors() + rayEngine.world.colors.sky.current = getSkyColorByTime() + rayEngine.currentShadingTransparencyMapValue = getBrightnessByTime() + rayEngine.world.colors.groundByTime = color.blend(rayEngine.world.colors.ground, 0x000000, rayEngine.currentShadingTransparencyMapValue) +end + +local function doDayNightCycle() + if rayEngine.world.dayNightCycle.enabled then + local computerUptime = computer.uptime() + if (computerUptime - rayEngine.world.dayNightCycle.lastComputerUptime) >= rayEngine.world.dayNightCycle.speed then + rayEngine.world.dayNightCycle.currentTime = rayEngine.world.dayNightCycle.currentTime + rayEngine.world.dayNightCycle.speed + if rayEngine.world.dayNightCycle.currentTime > rayEngine.world.dayNightCycle.length then rayEngine.world.dayNightCycle.currentTime = 0 end + rayEngine.world.dayNightCycle.lastComputerUptime = computerUptime + + rayEngine.refreshTimeDependentColors() + end + end +end + +local function convertWorldCoordsToMapCoords(x, y) + return number.round(x / rayEngine.properties.tileWidth), number.round(y / rayEngine.properties.tileWidth) +end + +local function getBlockCoordsByLook(distance) + local radRotation = math.rad(rayEngine.player.rotation) + return convertWorldCoordsToMapCoords(rayEngine.player.position.x + distance * math.sin(radRotation) * rayEngine.properties.tileWidth, rayEngine.player.position.y + distance * math.cos(radRotation) * rayEngine.properties.tileWidth) +end + +---------------------------------------------------- Работа с файлами ------------------------------------------------------------------ + +-- Загрузка параметров движка +function rayEngine.loadEngineProperties(pathToRayEnginePropertiesFile) + rayEngine.properties = filesystem.readTable(pathToRayEnginePropertiesFile) +end + +-- Загрузка конифгурации оружия +function rayEngine.loadWeapons(pathToWeaponsFolder) + rayEngine.weaponsFolder = pathToWeaponsFolder + rayEngine.weapons = filesystem.readTable(rayEngine.weaponsFolder .. "Weapons.cfg") + rayEngine.changeWeapon(1) +end + +-- Загрузка конкретного мира +function rayEngine.loadWorld(pathToWorldFolder) + rayEngine.world = filesystem.readTable(pathToWorldFolder .. "/World.cfg") + rayEngine.map = filesystem.readTable(pathToWorldFolder .. "/Map.cfg") + rayEngine.player = filesystem.readTable(pathToWorldFolder .. "/Player.cfg") + rayEngine.blocks = filesystem.readTable(pathToWorldFolder .. "/Blocks.cfg") + -- Дополняем карту ее размерами + rayEngine.map.width = #rayEngine.map[1] + rayEngine.map.height = #rayEngine.map + -- Ебашим правильную позицию игрока, основанную на этой ХУЙНЕ, которую ГЛЕБ так ЛЮБИТ + rayEngine.player.position.x = rayEngine.properties.tileWidth * rayEngine.player.position.x - rayEngine.properties.tileWidth / 2 + rayEngine.player.position.y = rayEngine.properties.tileWidth * rayEngine.player.position.y - rayEngine.properties.tileWidth / 2 + -- Рассчитываем цвета, зависимые от времени - небо, землю, стены + rayEngine.refreshTimeDependentColors() + -- Обнуляем текущее время, если превышен лимит, а то мало ли какой пидорас начнет править конфиги мира + rayEngine.world.dayNightCycle.currentTime = rayEngine.world.dayNightCycle.currentTime > rayEngine.world.dayNightCycle.length and 0 or rayEngine.world.dayNightCycle.currentTime + -- Осуществляем базовое получение аптайма пекарни + rayEngine.world.dayNightCycle.lastComputerUptime = computer.uptime() + -- Рассчитываем необходимые параметры движка + rayEngine.calculateAllParameters() + + -- rayEngine.wallsTexture = image.load("/heart.pic") + -- rayEngine.wallsTexture = image.transform(rayEngine.wallsTexture, rayEngine.properties.tileWidth, rayEngine.properties.tileWidth / 2) +end + +---------------------------------------------------- Функции, связанные с игроком ------------------------------------------------------------------ + +function rayEngine.changeWeapon(weaponID) + if rayEngine.weapons[weaponID] then + rayEngine.currentWeapon = { + ID = weaponID, + damage = rayEngine.weapons[weaponID].damage, + weaponTexture = image.load(rayEngine.weaponsFolder .. rayEngine.weapons[weaponID].weaponTexture), + fireTexture = image.load(rayEngine.weaponsFolder .. rayEngine.weapons[weaponID].fireTexture), + crosshairTexture = image.load(rayEngine.weaponsFolder .. rayEngine.weapons[weaponID].crosshairTexture) + } + rayEngine.calculateWeaponPosition() + else + rayEngine.currentWeapon = nil + end +end + +function rayEngine.move(distanceForward, distanceRight) + local forwardRotation = math.rad(rayEngine.player.rotation) + local rightRotation = math.rad(rayEngine.player.rotation + 90) + local xNew = rayEngine.player.position.x + distanceForward * math.sin(forwardRotation) + distanceRight * math.sin(rightRotation) + local yNew = rayEngine.player.position.y + distanceForward * math.cos(forwardRotation) + distanceRight * math.cos(rightRotation) + + local xWorld, yWorld = convertWorldCoordsToMapCoords(xNew, yNew) + if rayEngine.map[yWorld][xWorld] == nil then + rayEngine.player.position.x, rayEngine.player.position.y = xNew, yNew + end +end + +function rayEngine.rotate(angle) + rayEngine.player.rotation = constrainAngle(rayEngine.player.rotation + angle) +end + +function rayEngine.turnRight() + rayEngine.rotate(rayEngine.player.rotationSpeed) +end + +function rayEngine.turnLeft() + rayEngine.rotate(-rayEngine.player.rotationSpeed) +end + +function rayEngine.moveForward() + rayEngine.move(rayEngine.player.moveSpeed, 0) +end + +function rayEngine.moveBackward() + rayEngine.move(-rayEngine.player.moveSpeed, 0) +end + +function rayEngine.moveLeft() + rayEngine.move(0, -rayEngine.player.moveSpeed) +end + +function rayEngine.moveRight() + rayEngine.move(0, rayEngine.player.moveSpeed) +end + +function rayEngine.jump() + if not rayEngine.player.jumpTimer then + local function onJumpFinished() + rayEngine.horizonPosition = rayEngine.horizonPosition - rayEngine.player.jumpHeight; + rayEngine.horizonPosition = rayEngine.horizonPosition - rayEngine.player.jumpHeight; + rayEngine.player.jumpTimer = nil + end + + rayEngine.player.jumpTimer = event.timer(1, onJumpFinished) + rayEngine.horizonPosition = rayEngine.horizonPosition + rayEngine.player.jumpHeight + rayEngine.horizonPosition = rayEngine.horizonPosition + rayEngine.player.jumpHeight + end +end + +function rayEngine.crouch() + rayEngine.player.isCrouched = not rayEngine.player.isCrouched + local heightAdder = rayEngine.player.isCrouched and -rayEngine.player.crouchHeight or rayEngine.player.crouchHeight + rayEngine.horizonPosition = rayEngine.horizonPosition + heightAdder + rayEngine.horizonPosition = rayEngine.horizonPosition + heightAdder +end + +function rayEngine.destroy(distance) + local xBlock, yBlock = getBlockCoordsByLook(distance) + if rayEngine.map[yBlock] and rayEngine.map[yBlock][xBlock] and rayEngine.blocks[rayEngine.map[yBlock][xBlock]] and rayEngine.blocks[rayEngine.map[yBlock][xBlock]].canBeDestroyed then rayEngine.map[yBlock][xBlock] = nil end +end + +function rayEngine.place(distance, blockColor) + local xBlock, yBlock = getBlockCoordsByLook(distance) + if rayEngine.map[yBlock] and rayEngine.map[yBlock][xBlock] == nil then rayEngine.map[yBlock][xBlock] = blockColor end +end + +---------------------------------------------------- Функции интерфейса ------------------------------------------------------------------ + +function rayEngine.drawDebugInformation(x, y, width, transparency, ...) + local lines = {...} + screen.drawRectangle(x, y, width, #lines, 0x000000, 0x000000, " ", transparency); x = x + 1 + for line = 1, #lines do screen.drawText(x, y, 0xEEEEEE, lines[line]); y = y + 1 end +end + +local function drawFieldOfViewAngle(x, y, distance, color) + local fieldOfViewHalf = rayEngine.player.fieldOfView / 2 + local firstAngle, secondAngle = math.rad(-(rayEngine.player.rotation - fieldOfViewHalf)), math.rad(-(rayEngine.player.rotation + fieldOfViewHalf)) + local xFirst, yFirst = math.floor(x + math.sin(firstAngle) * distance), math.floor(y + math.cos(firstAngle) * distance) + local xSecond, ySecond = math.floor(x + math.sin(secondAngle) * distance), math.floor(y + math.cos(secondAngle) * distance) + screen.drawSemiPixelLine(x, y, xFirst, yFirst, color) + screen.drawSemiPixelLine(x, y, xSecond, ySecond, color) +end + +function rayEngine.drawMap(x, y, width, height, transparency) + local xHalf, yHalf = math.floor(width / 2), math.floor(height / 2) + local xMap, yMap = convertWorldCoordsToMapCoords(rayEngine.player.position.x, rayEngine.player.position.y) + + screen.drawRectangle(x, y, width, yHalf, 0x000000, 0x000000, " ", transparency) + + local xPos, yPos = x, y * 2 - 1 + for i = yMap - yHalf + 1, yMap + yHalf do + for j = xMap + xHalf + 1, xMap - xHalf + 2, -1 do + if rayEngine.map[i] and rayEngine.map[i][j] then + screen.semiPixelSet(xPos, yPos, rayEngine.blocks[rayEngine.map[i][j]].color) + end + xPos = xPos + 1 + end + xPos = x; yPos = yPos + 1 + end + + local xPlayer, yPlayer = x + xHalf, y + yHalf + --Поле зрения + if rayEngine.drawFieldOfViewOnMinimap then drawFieldOfViewAngle(xPlayer, yPlayer, 5, 0xCCFFBF) end + --Игрок + screen.semiPixelSet(xPlayer, yPlayer, 0x66FF40) +end + +function rayEngine.intro() + local logo = image.fromString("17060000FF 0000FF 0000FF 0000FF 007EFF▄007EFF▄007EFF▄007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀007EFF▄007EFF▄007EFF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 007EFF▄007EFF▀007EFF▀0000FF 0000FF 0000FF 0000FF 0053FF▄0053FF▀0053FF▀0053FF▀0053FF▄0000FF 0000FF 0000FF 0000FF 007EFF▀007EFF▀007EFF▄0000FF 0000FF 0000FF 007EFF▀007EFF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 530000 0000FF 0078FF▀0000FF 537800▀0078FF▀0078FF▀0078FF▀0078FF▀0078FF▀0078FF▀7E7800▀0078FF▀0000FF 0078FF▀0000FF 0000FF 007EFF▀007EFF▀007EFF▄007EFF▄007EFF▄0000FF 0000FF 0053FF▀0053FF▀0053FF▀0000FF 0000FF 007EFF▄007EFF▄007EFF▄007EFF▀007EFF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 007EFFP007EFFo007EFFw007EFFe007EFFr007EFFe007EFFd0000FF 007EFFb007EFFy0000FF 007EFFR007EFFa007EFFy007EFFE007EFFn007EFFg007EFFi007EFFn007EFFe007EFF™0000FF 0000FF ") + local x, y = math.floor(screen.getWidth() / 2 - logo[1] / 2), math.floor(screen.getHeight() / 2 - logo[2] / 2) + local function draw(transparency) + screen.clear(0xF0F0F0); + screen.drawImage(x, y, logo) + screen.drawRectangle(1, 1, screen.getWidth(), screen.getHeight(), 0x000000, 0x000000, " ", transparency) + screen.update() + event.sleep(0) + end + for i = 0, 100, 20 do draw(i) end + event.sleep(1.5) + for i = 100, 0, -20 do draw(i) end +end + +function rayEngine.compass(x, y) + if not rayEngine.compassImage then rayEngine.compassImage = image.fromString("1C190000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 553600▄373100▄543600▄373600▄543600▄373100▄540000 375400▄673700▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0055FF▄675400▄553700▄375500▄550000 375500▄540000 375400▄540000 373600▄310000 675500▄677E00▄375300▄365400▄373600▄540000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 555400▄553700▄540000 543700▄540000 375300▄533100▄310B00▄310000 310000 360000 543100▄375300▄553100▄533600▄543200▄313600▄372A00▄373100▄0054FF▄0054FF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 540000 543200▄540000 545300▄540600▄063100▄310000 315400▄365400▄373100▄313600▄530000 0000FF 0000FF 535400▄535400▄540000 365300▄533100▄065300▄310000 530600▄0054FF▄0000FF 0000FF 0000FF 0000FF 0000FF 530000 365300▄543600▄310600▄312A00▄2A5300▄365300▄543600▄540000 365300▄315300▄530000 0000FF 0000FF 535400▄540000 540000 313600▄363100▄540000 313600▄315300▄062A00▄543100▄0000FF 0000FF 0000FF 0000FF 315300▄533600▄312A00▄2A3100▄315300▄533600▄315400▄540000 535400▄540000 530000 533100▄0000FF 0000FF 540000 540000 315400▄540000 543100▄530000 543100▄313600▄2A0000 2A2900▄540000 0000FF 0000FF 0000FF 533100▄315400▄530000 062A00▄533100▄315300▄535400▄533100▄540000 315400▄543100▄530000 0000FF 0000FF 540000 535400▄315300▄535400▄363100▄535400▄530000 530000 312A00▄2A5300▄0054FF▀0000FF 0000FF 0000FF 312A00▄530000 553600▄2A5500▄2A0000 312A00▄533600▄545300▄315400▄535400▄540000 540000 0053FF▄0053FF▄540000 530000 535400▄535400▄545300▄530000 363100▄312A00▄313600▄545300▄0000FF 0000FF 0000FF 0000FF 530000 535400▄540000 545300▄555400▄315500▄315400▄533100▄543100▄530000 533100▄530000 535400▄535500▄533100▄535400▄315300▄533100▄533600▄315300▄530000 542A00▄312800▄0029FF▀0000FF 0000FF 0000FF 0000FF 530000 545500▄540000 555300▄535400▄530000 542A00▄545300▄365400▄530000 543600▄2A5400▄547E00▄550000 545300▄533600▄540000 530000 530000 542A00▄2A0000▄0029FF▀0000FF 0000FF 0000FF 0000FF 0000FF 530000 535400▄540000 540000 540000 545300▄530000 540000 2A5500▄545300▄292A00▄290000 292A00▄290000 295400▄292A00▄292A00▄290000 542A00▄543600▄285400▄0054FF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 530000 533100▄540000 530000 535400▄0053FF▀0053FF▀542800▄542800▄532800▄2A2900▄542900▄532900▄542900▄542900▄532900▄542800▄2A2900▄2A2800▄532900▄552800▄542800▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 2A5300▄530000 535400▄540000 530000 532A00▀2A5300▄2A5500▄0029FF▀0028FF▀0028FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0028FF▀0028FF▀0028FF▀295300▄535500▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 535400▄545300▄530000 545300▄530000 2A0000 2A5300▄545300▄532A00▄542900▄532900▄2A2900▄2A2900▄2A2800▄2A2800▄2A2800▄2A2900▄2A2900▄532900▄532900▄2A2900▄542A00▄545300▄0054FF▄0054FF▄0000FF 0000FF 0000FF 545300▄530000 530000 545300▄2A5300▄532A00▄542900▄290000 295400▄297F00▄548100▄548100▄558100▄558100▄558100▄558100▄538100▄2A8100▄007E00▄295400▄2A2900▄295400▄2A2900▄290000 542A00▄557E00▄0000FF 0000FF 530000 2A0000 545300▄530000 532900▄285400▄2A8000▄7F8100▄810000 810000 810000 810000 810000 810000 812A00N810000 810000 810000 810000 810000 808100▄548100▄545500▄295300▄282900▄542900▄0000FF 0000FF 2A0000 2A0000 545300▄2A2900▄298000▄810000 810000 815500381550018155005810000 810000 810000 810000 810000 810000 810000 810000 81550048155005810000 810000 810000 558100▄2A5300▄282900▄0029FF▄0000FF 532A00▄2A0000 545300▄547E00▄810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 7F8000▄290000 292800▄0000FF 2A0000 2A0000 2A5300▄7E5300▄810000 812A00W810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 812A00E810000 807E00▄2A2900▄292800▄0000FF 2A0000 2A2900▄2A0000 2A0000 552A00▄810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 815500▄542900▄280000 0028FF▀530000 2A0000 2A0000 290000 2A2900▄545300▄552A00▄815500▄810000 815500281550028155005810000 810000 810000 810000 810000 810000 810000 815500181550038155005817F00▄552900▄292800▄280000 282A00▄0000FF 530000 2A0000 540000 2A5300▄282A00▄290000 532900▄542A00▄542800▄552900▄7F5300▄815500▄817F00▄817F00▄810000 812A00S810000 817F00▄815500▄815500▄555400▄532900▄292800▄280000 282A00▄282A00▄530000 0000FF 532A00▄2A0000 530000 530000 2A5300▄290000 282900▄002900▄280000 280000 280000 290000 292A00▄2A2900▄542900▄532900▄2A0000 295300▄282900▄280000 280000 280000 282900▄295300▄295300▄2A5300▄552A00▄0000FF 530000 295300▄535400▄540000 535400▄540000 535400▄2A5500▄282900▄280000▄280000 280000▄290000▄2A2800▄2A2900▄552A00▄7E0000▄2A0000▄280000▄280000 280000▄280000▄002AFF▀002AFF▀002AFF▀002AFF▀0000FF 0000FF 532900▄532800▄0029FF▀0029FF▀0029FF▀0029FF▀0029FF▀0029FF▀0000FF 2A9800▄285500▄547E00▄7E5400▄7F5300▄7E2900▄7E2900▄552A00▄542A00▄2A5300▄282A00▄2A7E00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF ") end + screen.drawImage(x, y, rayEngine.compassImage) + + x, y = x + 15, y + 17 + local distance = 3.4 + local northAngle = -rayEngine.player.rotation + local xScaleFactor = 2.2 + local southPoint, northPoint = {}, {} + local northAngleRad = math.rad(northAngle) + northPoint.x, northPoint.y = number.round(x + math.sin(northAngleRad) * distance * xScaleFactor), number.round(y - math.cos(northAngleRad) * distance) + northAngleRad = math.rad(northAngle + 180) + southPoint.x, southPoint.y = number.round(x + math.sin(northAngleRad) * distance * xScaleFactor), number.round(y - math.cos(northAngleRad) * distance) + + y = y * 2 + screen.drawSemiPixelLine(x, y, northPoint.x, northPoint.y * 2, 0xFF5555) + screen.drawSemiPixelLine(x, y, southPoint.x, southPoint.y * 2, 0xFFFFFF) + screen.semiPixelSet(x, y, 0x000000) +end + +function rayEngine.watch(x, y) + if not rayEngine.watchImage then rayEngine.watchImage = image.fromString("20190000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0053FF▄552900▄673100▄7E2A00▄7E3100▄7E2A00▄7E3100▄672A00▄7E2A00▄672900▄7F3100▄7E2A00▄0053FF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 313600▄290000 290600▄062900▄290000 062900▄290000 062900▄290000 290600▄062900▄2A2900▄2A0600▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 313600▄012900▄2A2900▄290000 012900▄290000 290000 012A00▄290000 290000 312900▄293100▄310600▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 310000 292800▄062A00▄290000 290100▄062900▄290000 290000 2C2900▄290600▄293100▄2A2900▄292800▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0054FF▄557F00▄012800▄290000 290000 292800▄290000 012900▄292800▄290600▄290000 292800▄290000 012800▄807E00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF AB8100▄7F8000▄283100▄280000 015400▄318100▄005300▄065500▄315500▄282A00▄296700▄015500▄290000 002900▄677E00▄81AA00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 807F00▄7F8000▄677E00▄812900▄672800▄542800▄2A0000▄2A0000▄815400▄ACAA00▄555400▄283100▄2A0000 312900▄672800▄7F5300▄7F0000 7F0000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 007EFF▄817E00▄7E2900▄2A0000▄805300▄54AA00▄003100▄2A0000 310600▄532900▄312800▄532900▄360100▄552900▄672800▄7E2A00▄315500▄678000▄555300▄540000▄805400▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0081FF▄802900▄280100▄280000 285400▄310600▄532900▄290000 290000 290000 290000 29D700129D7002290000 290000 290000 282900▄062900▄2A2900▄550600▄556700▄285500▄542800▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0080FF▄557E00▄28AA00▄005500▄552900▄290000 290000 290000 29D700129D7001290000 290000 290000 290000 290000 290000 290000 29D7001290000 290000 290000 290000 672900▄318000▄297F00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 543100▄002800▄553100▄7E2800▄290000 29D700129D7000290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 29D7002290000 290000 7E2900▄7E6700▄282900▄808100▄0000FF 0000FF 0000FF 0000FF 805500▄280000▄290000 310600▄290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 555400▄535400▄533100▄D58000▄0080FF▄0000FF 552900▀550000 54AB00▄558100▄290000 290000 29D7009290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 29D7003290000 315400▄548100▄067E00▄557F00▄806700▄ACAB00▄0055FF▀545500▄AB3100▄815400▄290100▄290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 545300▄AB5500▄815300▄558000▄558000▄0000FF 0000FF 538100▄002800▄290600▄290000 290000 290000 29D7008290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 29D7004290000 290000 530000 000000 286700▄0080FF▀0000FF 0000FF 0000FF 0000FF 292A00▄000100▄530000 285400▄290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 296700▄063100▄280000 818000▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 550000 810000▄532800▄015300▄280000 292800▄290000 29D7007290000 290000 290000 290000 290000 290000 290000 290000 290000 29D7005290000 290100▄292A00▄065300▄7F0000▄802900▄81C900▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 549900▄002900▄292800▄312800▄290600▄283100▄290600▄292800▄290000 290000 290000 29D7006290000 290000 292800▄292800▄290000 285300▄2A0600▄362800▄280000 285500▄0081FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 7EAA00▄317E00▄005300▄547E00▄7F2900▄290000▄292A00▄063100▄283100▄295500▄286200▄285500▄012A00▄292A00▄310600▄540000▄807E00▄005500▄015500▄530000 0081FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 7E8100▄7E8000▄557F00▄317E00▄2A3600▄283100▄005400▄2A5300▄817E00▄AA7E00▄545300▄005300▄005400▄283100▄537E00▄548100▄7F0000 7E8000▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 819800▄807F00▄280000 002900▄312900▄7E2800▄292800▄532800▄552900▄280000 550100▄542800▄282900▄000100▄7E0000 AA9800▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 555300▄012800▄290000 062900▄290000 062900▄290000 062900▄290000 290000 290000 290600▄280000 007EFF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 313600▄062900▄312900▄290000 012900▄293100▄290000 062900▄312900▄290600▄312900▄2A0000 2A2900▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 315400▄290600▄310000 062900▄290000 293100▄062900▄290000 293100▄290000 293100▄062900▄312A00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0054FF▀285500▄285500▄015500▄285500▄285500▄015500▄295500▄015500▄285500▄015500▄285500▄065500▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF ") end + + screen.drawImage(x, y, rayEngine.watchImage) + x, y = x + 15, y + 12 + + local realTimeInSeconds = rayEngine.world.dayNightCycle.currentTime * 86400 / rayEngine.world.dayNightCycle.length + local hours = realTimeInSeconds / 3600 + local _, minutes = math.modf(hours) + local hourAngle = math.rad(hours * 360 / 12) + local minuteAngle = math.rad(minutes * 360) + local hourArrowLength, minuteArrowLength = 2.8, 4.5 + local xMinute, yMinute = number.round(x + math.sin(minuteAngle) * minuteArrowLength * 2), number.round(y - math.cos(minuteAngle) * minuteArrowLength) + local xHour, yHour = number.round(x + math.sin(hourAngle) * hourArrowLength * 2), number.round(y - math.cos(hourAngle) * hourArrowLength) + + y = y * 2 + screen.drawSemiPixelLine(x, y, xMinute, yMinute * 2, 0xEEEEEE) + screen.drawSemiPixelLine(x, y, xHour, yHour * 2, 0xEEEEEE) +end + +local function addItemToChatHistory(text, color) + text = text.wrap({text}, rayEngine.chatPanelWidth - 2) + table.insert(rayEngine.chatHistory, {color = color, text = text}) + if #rayEngine.chatHistory > rayEngine.chatHistoryLimit then table.remove(rayEngine.chatHistory, 1) end +end + +function rayEngine.chat(transparency) + local x, y = 1, screen.getHeight() - rayEngine.chatPanelHeight - 3 + screen.drawRectangle(x, y, rayEngine.chatPanelWidth, rayEngine.chatPanelHeight, 0x000000, 0xFFFFFF, " ", transparency or 0.5) + screen.setDrawLimit(x, y, x + rayEngine.chatPanelWidth - 1, y + rayEngine.chatPanelHeight - 1) + local yMessage = y + rayEngine.chatPanelHeight - 1 + x = x + 1 + + for message = #rayEngine.chatHistory, 1, -1 do + for line = #rayEngine.chatHistory[message].text, 1, -1 do + screen.drawText(x, yMessage, rayEngine.chatHistory[message].color or 0xFFFFFF, rayEngine.chatHistory[message].text[line]) + yMessage = yMessage - 1 + if yMessage < y then screen.resetDrawLimit(); return end + end + end + + screen.resetDrawLimit() +end + +function rayEngine.commandLine(transparency) + transparency = transparency or 50 + local inputPanelHeight = 3 + local x, y = 1, screen.getHeight() - inputPanelHeight + 1 + --Врубаем чат и рисуем все, включая его + rayEngine.chatEnabled = true + rayEngine.update() + + --Ввод данных + local input = GUI.input(x, y, screen.getWidth(), 3, 0xFFFFFF, 0x3C3C3C, 0x666666, 0xFFFFFF, 0x3C3C3C, "") + input.eventHandler({draw = function() input:draw() end}, input, "touch", input.x, input.y) + + local words = {}; for word in string.gmatch(input.text, "[^%s]+") do table.insert(words, unicode.lower(word)) end + if #words > 0 then + if unicode.sub(words[1], 1, 1) == "/" then + words[1] = unicode.sub(words[1], 2, -1) + if words[1] == "time" then + if words[2] == "set" and words[3] and tonumber(words[3]) then + local newTime = tonumber(words[3]) + if newTime < 0 or newTime > rayEngine.world.dayNightCycle.length then + addItemToChatHistory("Время не может быть отрицательным и превышать длину суток (" .. rayEngine.world.dayNightCycle.length .. " секю)", 0xFF8888) + else + rayEngine.world.dayNightCycle.currentTime = math.floor(newTime) + addItemToChatHistory("Время успешно изменено на: " .. newTime, 0xFFDB40) + end + elseif words[2] == "get" then + addItemToChatHistory("Текущее время: " .. rayEngine.world.dayNightCycle.currentTime, 0xFFDB40) + addItemToChatHistory("Длина суток: " .. rayEngine.world.dayNightCycle.length, 0xFFDB40) + elseif words[2] == "lock" then + rayEngine.world.dayNightCycle.enabled = not rayEngine.world.dayNightCycle.enabled + addItemToChatHistory("Состояние цикла дня и ночи: " .. tostring(rayEngine.world.dayNightCycle.enabled), 0xFFDB40) + end + elseif words[1] == "setrenderquality" and tonumber(words[2]) then + rayEngine.properties.raycastQuality = tonumber(words[2]) + addItemToChatHistory("Качество рендера изменено на: " .. tonumber(words[2]), 0xFFDB40) + elseif words[1] == "setdrawdistance" and tonumber(words[2]) then + rayEngine.properties.drawDistance = tonumber(words[2]) + addItemToChatHistory("Дистанция прорисовки изменена на: " .. tonumber(words[2]), 0xFFDB40) + elseif words[1] == "setshadingcascades" and tonumber(words[2]) then + rayEngine.properties.shadingCascades = tonumber(words[2]) + addItemToChatHistory("Количество цветов для отрисовки блока изменено на: " .. tonumber(words[2]), 0xFFDB40) + elseif words[1] == "setshadingdistance" and tonumber(words[2]) then + rayEngine.properties.shadingDistance = tonumber(words[2]) + addItemToChatHistory("Дистация затенения блоков изменена на: " .. tonumber(words[2]), 0xFFDB40) + elseif words[1] == "help" then + addItemToChatHistory("Доступные команды:", 0xFFDB40) + addItemToChatHistory("/time get", 0xFFFFBF) + addItemToChatHistory("/time set ", 0xFFFFBF) + addItemToChatHistory("/time lock", 0xFFFFBF) + addItemToChatHistory(" ", 0xFFFFFF) + addItemToChatHistory("/setRenderQuality ", 0xFFFFBF) + addItemToChatHistory("/setDrawDistance ", 0xFFFFBF) + addItemToChatHistory("/setShadingCascades ", 0xFFFFBF) + addItemToChatHistory("/setShadingDistance ", 0xFFFFBF) + else + addItemToChatHistory("Неизвестная команда. Введите /help для получения списка команд", 0xFF8888) + end + else + addItemToChatHistory("> " .. input.text, 0xFFFFFF) + end + end + + --Активируем таймер + if rayEngine.chatTimer then event.cancel(rayEngine.chatTimer) end + rayEngine.chatEnabled = true + rayEngine.chatTimer = event.timer(rayEngine.chatShowTime, function() rayEngine.chatEnabled = false; rayEngine.chatTimer = nil; update() end) +end + +function rayEngine.toggleMinimap() + rayEngine.minimapEnabled = not rayEngine.minimapEnabled +end + +function rayEngine.toggleDebugInformation() + rayEngine.debugInformationEnabled = not rayEngine.debugInformationEnabled +end + +function rayEngine.toggleCompass() + rayEngine.compassEnabled = not rayEngine.compassEnabled + if not rayEngine.compassEnabled then rayEngine.compassImage = nil end +end + +function rayEngine.toggleWatch() + rayEngine.watchEnabled = not rayEngine.watchEnabled + if not rayEngine.watchEnabled then rayEngine.watchImage = nil end +end + +function rayEngine.drawWeapon() + if rayEngine.currentWeapon.needToFire then screen.drawImage(rayEngine.currentWeapon.xFire, rayEngine.currentWeapon.yFire, rayEngine.currentWeapon.fireTexture); rayEngine.currentWeapon.needToFire = false end + screen.drawImage(rayEngine.currentWeapon.xWeapon, rayEngine.currentWeapon.yWeapon, rayEngine.currentWeapon.weaponTexture) + screen.drawImage(rayEngine.currentWeapon.xCrosshair, rayEngine.currentWeapon.yCrosshair, rayEngine.currentWeapon.crosshairTexture) +end + +function rayEngine.drawStats() + local width = math.floor(screen.getWidth() * 0.3) + local height = 5 + local x, y = screen.getWidth() - width - 1, 2 + screen.drawRectangle(x, y, width, height, 0x000000, 0xFFFFFF, " ", 0.5) + + GUI.progressBar(x + 1, y + 4, width - 2, 1, 0x000000, 0xFF5555, rayEngine.player.health.current, rayEngine.player.health.maximum, true) +end + +---------------------------------------------------- Функции отрисовки мира ------------------------------------------------------------------ + +local function raycast(angle) + angle = math.rad(angle) + local angleSinDistance, angleCosDistance, currentDistance, xWorld, yWorld, xMap, yMap, tile = math.sin(angle) * rayEngine.properties.raycastQuality, math.cos(angle) * rayEngine.properties.raycastQuality, 0, rayEngine.player.position.x, rayEngine.player.position.y + + while true do + if currentDistance <= rayEngine.properties.drawDistance then + xMap, yMap = math.floor(xWorld / rayEngine.properties.tileWidth), math.floor(yWorld / rayEngine.properties.tileWidth) + if rayEngine.map[yMap] and rayEngine.map[yMap][xMap] then + return currentDistance, rayEngine.map[yMap][xMap] + end + + xWorld, yWorld = xWorld + angleSinDistance, yWorld + angleCosDistance + currentDistance = currentDistance + rayEngine.properties.raycastQuality + else + return nil + end + end +end + +function rayEngine.drawWorld() + --Земля + screen.clear(rayEngine.world.colors.groundByTime) + --Небо + screen.drawRectangle(1, 1, screen.getWidth(), rayEngine.horizonPosition, rayEngine.world.colors.sky.current, 0x0, " ") + --Сцена + local startAngle, endAngle, startX, distanceToTile, tileID, height, startY, tileColor = rayEngine.player.rotation - rayEngine.player.fieldOfView / 2, rayEngine.player.rotation + rayEngine.player.fieldOfView / 2, 1 + for angle = startAngle, endAngle, rayEngine.raycastStep do + distanceToTile, tileID = raycast(angle) + if distanceToTile then + -- Получаем цвет стенки + tileColor = getTileColor(rayEngine.blocks[tileID].color, distanceToTile) + + -- Поддержка "высококачественной" doubleHeight-графики + if rayEngine.properties.useSimpleRenderer then + height = rayEngine.properties.tileWidth / distanceToTile * rayEngine.distanceToProjectionPlane + startY = rayEngine.horizonPosition - height / 2 + 1 + screen.drawRectangle(math.floor(startX), math.floor(startY), 1, math.floor(height), tileColor, 0x000000, " ") + else + height = rayEngine.properties.tileWidth / distanceToTile * rayEngine.distanceToProjectionPlane * 2 + startY = rayEngine.horizonPosition * 2 - height / 2 + 1 + screen.drawSemiPixelRectangle(math.floor(startX), math.floor(startY), 1, height, tileColor) + end + + --ТИКСТУРКА)))00 + -- local xTexture = startX % rayEngine.properties.tileWidth + 1 + -- if xTexture >= 1 and xTexture <= screen.getWidth() then + -- local column = image.getColumn(rayEngine.wallsTexture, xTexture) + -- column = image.transform(column, 1, height) + -- screen.drawImage(math.floor(startX), math.floor(startY), column) + -- end + end + startX = startX + 1 + end +end + +function rayEngine.update() + local frameRenderClock = os.clock() + + rayEngine.drawWorld() + if rayEngine.currentWeapon then rayEngine.drawWeapon() end + if rayEngine.minimapEnabled then rayEngine.drawMap(3, 2, 24, 24, 0.5) end + -- rayEngine.drawStats() + local xTools, yTools = 3, screen.getHeight() - 25 + if rayEngine.compassEnabled then rayEngine.compass(xTools, yTools); xTools = xTools + 30 end + if rayEngine.watchEnabled then rayEngine.watch(xTools, yTools) end + if rayEngine.chatEnabled then rayEngine.chat() end + doDayNightCycle() + + if rayEngine.debugInformationEnabled then + rayEngine.drawDebugInformation(3, 2 + (rayEngine.minimapEnabled and 12 or 0), 24, 0.6, + "renderTime: " .. string.format("%.2f", (os.clock() - frameRenderClock) * 1000) .. " ms", + "freeRAM: " .. string.format("%.2f", computer.freeMemory() / 1024) .. " KB", + "pos: " .. string.format("%.2f", rayEngine.player.position.x) .. " x " .. string.format("%.2f", rayEngine.player.position.y) + ) + end + + screen.update() +end + +---------------------------------------------------------------------------------------------------------------------------------- + +function rayEngine.changeResolution(width, height) + screen.setResolution(width, height) + rayEngine.calculateAllParameters() +end + +function rayEngine.fire() + rayEngine.currentWeapon.needToFire = true + rayEngine.update() + event.sleep(0.1) + rayEngine.update() +end + +---------------------------------------------------------------------------------------------------------------------------------- + +return rayEngine diff --git a/Applications/RayWalk/Weapons/CrosshairTextures/Angled.pic b/Applications/RayWalk.app/Weapons/CrosshairTextures/Angled.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/RayWalk/Weapons/CrosshairTextures/Angled.pic rename to Applications/RayWalk.app/Weapons/CrosshairTextures/Angled.pic diff --git a/Applications/RayWalk/Weapons/CrosshairTextures/Default.pic b/Applications/RayWalk.app/Weapons/CrosshairTextures/Default.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/RayWalk/Weapons/CrosshairTextures/Default.pic rename to Applications/RayWalk.app/Weapons/CrosshairTextures/Default.pic diff --git a/Applications/RayWalk/Weapons/CrosshairTextures/Dotted.pic b/Applications/RayWalk.app/Weapons/CrosshairTextures/Dotted.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/RayWalk/Weapons/CrosshairTextures/Dotted.pic rename to Applications/RayWalk.app/Weapons/CrosshairTextures/Dotted.pic diff --git a/Applications/RayWalk/Weapons/CrosshairTextures/Half.pic b/Applications/RayWalk.app/Weapons/CrosshairTextures/Half.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/RayWalk/Weapons/CrosshairTextures/Half.pic rename to Applications/RayWalk.app/Weapons/CrosshairTextures/Half.pic diff --git a/Applications/RayWalk/Weapons/FireTextures/Plasma.pic b/Applications/RayWalk.app/Weapons/FireTextures/Plasma.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/RayWalk/Weapons/FireTextures/Plasma.pic rename to Applications/RayWalk.app/Weapons/FireTextures/Plasma.pic diff --git a/Applications/RayWalk/Weapons/FireTextures/PowderFire.pic b/Applications/RayWalk.app/Weapons/FireTextures/PowderFire.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/RayWalk/Weapons/FireTextures/PowderFire.pic rename to Applications/RayWalk.app/Weapons/FireTextures/PowderFire.pic diff --git a/Applications/RayWalk/Weapons/WeaponTextures/Pistol.pic b/Applications/RayWalk.app/Weapons/WeaponTextures/Pistol.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/RayWalk/Weapons/WeaponTextures/Pistol.pic rename to Applications/RayWalk.app/Weapons/WeaponTextures/Pistol.pic diff --git a/Applications/RayWalk/Weapons/WeaponTextures/Plasmer.pic b/Applications/RayWalk.app/Weapons/WeaponTextures/Plasmer.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/RayWalk/Weapons/WeaponTextures/Plasmer.pic rename to Applications/RayWalk.app/Weapons/WeaponTextures/Plasmer.pic diff --git a/Applications/RayWalk/Weapons/WeaponTextures/Rifle.pic b/Applications/RayWalk.app/Weapons/WeaponTextures/Rifle.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/RayWalk/Weapons/WeaponTextures/Rifle.pic rename to Applications/RayWalk.app/Weapons/WeaponTextures/Rifle.pic diff --git a/Applications/RayWalk/Weapons/WeaponTextures/Sniper.pic b/Applications/RayWalk.app/Weapons/WeaponTextures/Sniper.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/RayWalk/Weapons/WeaponTextures/Sniper.pic rename to Applications/RayWalk.app/Weapons/WeaponTextures/Sniper.pic diff --git a/Applications/RayWalk/Weapons/Weapons.cfg b/Applications/RayWalk.app/Weapons/Weapons.cfg similarity index 100% rename from Applications/RayWalk/Weapons/Weapons.cfg rename to Applications/RayWalk.app/Weapons/Weapons.cfg diff --git a/Applications/RayWalk/Worlds/ExampleWorld/Blocks.cfg b/Applications/RayWalk.app/Worlds/ExampleWorld/Blocks.cfg similarity index 100% rename from Applications/RayWalk/Worlds/ExampleWorld/Blocks.cfg rename to Applications/RayWalk.app/Worlds/ExampleWorld/Blocks.cfg diff --git a/Applications/RayWalk/Worlds/ExampleWorld/Map.cfg b/Applications/RayWalk.app/Worlds/ExampleWorld/Map.cfg similarity index 100% rename from Applications/RayWalk/Worlds/ExampleWorld/Map.cfg rename to Applications/RayWalk.app/Worlds/ExampleWorld/Map.cfg diff --git a/Applications/RayWalk/Worlds/ExampleWorld/Player.cfg b/Applications/RayWalk.app/Worlds/ExampleWorld/Player.cfg similarity index 100% rename from Applications/RayWalk/Worlds/ExampleWorld/Player.cfg rename to Applications/RayWalk.app/Worlds/ExampleWorld/Player.cfg diff --git a/Applications/RayWalk/Worlds/ExampleWorld/World.cfg b/Applications/RayWalk.app/Worlds/ExampleWorld/World.cfg similarity index 100% rename from Applications/RayWalk/Worlds/ExampleWorld/World.cfg rename to Applications/RayWalk.app/Worlds/ExampleWorld/World.cfg diff --git a/Applications/RayWalk/Worlds/SundownBeams/Blocks.cfg b/Applications/RayWalk.app/Worlds/SundownBeams/Blocks.cfg similarity index 100% rename from Applications/RayWalk/Worlds/SundownBeams/Blocks.cfg rename to Applications/RayWalk.app/Worlds/SundownBeams/Blocks.cfg diff --git a/Applications/RayWalk/Worlds/SundownBeams/Map.cfg b/Applications/RayWalk.app/Worlds/SundownBeams/Map.cfg similarity index 100% rename from Applications/RayWalk/Worlds/SundownBeams/Map.cfg rename to Applications/RayWalk.app/Worlds/SundownBeams/Map.cfg diff --git a/Applications/RayWalk/Worlds/SundownBeams/Player.cfg b/Applications/RayWalk.app/Worlds/SundownBeams/Player.cfg similarity index 100% rename from Applications/RayWalk/Worlds/SundownBeams/Player.cfg rename to Applications/RayWalk.app/Worlds/SundownBeams/Player.cfg diff --git a/Applications/RayWalk/Worlds/SundownBeams/World.cfg b/Applications/RayWalk.app/Worlds/SundownBeams/World.cfg similarity index 100% rename from Applications/RayWalk/Worlds/SundownBeams/World.cfg rename to Applications/RayWalk.app/Worlds/SundownBeams/World.cfg diff --git a/Applications/RayWalk/Icon.pic b/Applications/RayWalk/Icon.pic deleted file mode 100644 index c1b3c372190abfb407a7881894e856ef86b9a6bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 135 zcmXYou?>JQ3HgMfnOzVJgYPLyL?mMJ zfo%#3;8;fILqmi7Tv3St26|4YW(*59MHUNs+Er+-A{{CU=o9%ud<*L5t!N*501S^E A_W%F@ diff --git a/Applications/RayWalk/RayEngine.cfg b/Applications/RayWalk/RayEngine.cfg deleted file mode 100755 index ed061679..00000000 --- a/Applications/RayWalk/RayEngine.cfg +++ /dev/null @@ -1,28 +0,0 @@ -{ - ["useSimpleRenderer"] = true, - ["shadingCascades"] = 8, - ["shadingDistance"] = 500, - ["shadingTransparencyMap"] = { - [1] = 0.2, - [2] = 0.4, - [3] = 0.6, - [4] = 0.8, - [5] = 1.0, - [6] = 1.0, - [7] = 1.0, - [8] = 0.8, - [9] = 0.8, - [10] = 0.70196078431373, - [11] = 0.6, - [12] = 0.50196078431373, - [13] = 0.4, - [14] = 0.30196078431373, - }, - ["raycastQuality"] = 6.4, - ["screenResolution"] = { - ["width"] = 160, - ["height"] = 50 - }, - ["drawDistance"] = 1000, - ["tileWidth"] = 32 -} \ No newline at end of file diff --git a/Applications/Reinstall OS.app/Icon.pic b/Applications/Reinstall OS.app/Icon.pic new file mode 100644 index 0000000000000000000000000000000000000000..775a7b662ff06d229468b131751f0507383945dc GIT binary patch literal 185 zcmXwy!41Md3j5Ygz`bWpoCl~0{BpjE5Qzz->k>lJs-zYE{c?P zHcv=6K#;9okvNd>CsU30i;rLFQkuto6;W!=N)VS literal 0 HcmV?d00001 diff --git a/Applications/Reinstall OS.app/Main.lua b/Applications/Reinstall OS.app/Main.lua new file mode 100644 index 00000000..8674dca2 --- /dev/null +++ b/Applications/Reinstall OS.app/Main.lua @@ -0,0 +1,4 @@ + +if require("Internet").run("https://raw.githubusercontent.com/IgorTimofeev/MineOSStandalone/master/Installer/Main.lua") == nil then + computer.shutdown(true) +end diff --git a/Applications/Robot/GeoMiner/Full.lua b/Applications/Robot/GeoMiner/Full.lua deleted file mode 100644 index 8141754b..00000000 --- a/Applications/Robot/GeoMiner/Full.lua +++ /dev/null @@ -1,319 +0,0 @@ -local computer, component = require("computer"), require("component") - -local minDensity, maxDensity, chunkCount, worldHeight, droppables, tools, mathAbs, getComponent, getEnergy, checkItemInTable = - 2, - 10, - 9, - -100, - { - cobblestone = 1, - sandstone = 1, - stone = 1, - dirt = 1, - gravel = 1, - hardened_clay = 1, - nether_brick = 1, - sand = 1, - soul_sand = 1, - netherrack = 1, - }, - { - diamond_pickaxe = 1, - iron_pickaxe = 1, - }, - math.abs, - function(c) - c = component.list(c)() - return c and component.proxy(c) or nil - end, - function() - return computer.energy() / computer.maxEnergy() - end, - function(table, name) - return table[name] or table[name:gsub("minecraft:", "")] - end - -local robot, geolyzer, inventory_controller, generator = - getComponent("robot"), - getComponent("geolyzer"), - getComponent("inventory_controller"), - getComponent("generator") - -local positionX, positionY, positionZ, rotation, inventorySize, robotSwing, robotSelect, geolyzerScan, inventory_controllerGetStackInInternalSlot = 0, 0, 0, 0, robot.inventorySize(), robot.swing, robot.select, geolyzer.scan, inventory_controller.getStackInInternalSlot - -local turn, move = - function(clockwise) - robot.turn(clockwise) - rotation = rotation + (clockwise and 1 or -1) - rotation = rotation > 3 and 0 or rotation < 0 and 3 or rotation - end, - function(direction) - while true do - local success, reason = robotSwing(direction) - if success or reason == "air" then - success, reason = robot.move(direction) - if success then - if direction == 0 or direction == 1 then - positionY = positionY + (direction == 1 and 1 or -1) - else - positionX, positionZ = positionX + (rotation == 0 and 1 or rotation == 2 and -1 or 0), positionZ + (rotation == 1 and 1 or rotation == 3 and -1 or 0) - end - - - break - end - else - if reason == "block" then - while true do - computer.beep(1500, 1) - end - end - end - end - end - -local function turnTo(requiredRotation) - local difference = rotation - requiredRotation - if difference ~= 0 then - local fastestWay = difference > 2 - if difference <= 0 then - fastestWay = -difference <= 2 - end - - while rotation ~= requiredRotation do - turn(fastestWay) - end - end -end - -local moveTo, drop = - function(toX, toY, toZ) - toX, toY, toZ = toX - positionX, toY - positionY, toZ - positionZ - - if toY ~= 0 then - for i = 1, mathAbs(toY) do - move(toY > 0 and 1 or 0) - end - end - - if toX ~= 0 then - turnTo(toX > 0 and 0 or 2) - for i = 1, mathAbs(toX) do - move(3) - end - end - - if toZ ~= 0 then - turnTo(toZ > 0 and 1 or 3) - for i = 1, mathAbs(toZ) do - move(3) - end - end - end, - function(trashOnly) - local freeSlots = 0 - for i = 1, inventorySize do - local stack = inventory_controllerGetStackInInternalSlot(i) - if stack then - local droppable = checkItemInTable(droppables, stack.name) - if trashOnly and droppable or not trashOnly and not checkItemInTable(tools, stack.name) then - robotSelect(i) - robot.drop(droppable and 0 or 3) - freeSlots = freeSlots + 1 - end - end - end - - return freeSlots - end - -local function moveToBase() - --print("Пиздую на базу") - moveTo(0, positionY, 0) - moveTo(0, 0, 0) - - --print("Ищу сундук") - for i = 0, 3 do - local size = inventory_controller.getInventorySize(3) - if size and size > 3 then - --print("Нашел, дропаю весь шмот") - drop() - return - else - turn(true) - end - end - - --print("Чет сундука нет, дропну хоть треш") - drop(true) -end - ---print("Запускаю софтину") -robotSelect(1) -move(0) - ---print("Детекчу сторону") -local initial = geolyzerScan(1, 0)[33] -for i = 0, 3 do - if initial > 0 then - if robotSwing(3) and geolyzerScan(1, 0)[33] == 0 then - break - end - else - if robot.place(3) and geolyzerScan(1, 0)[33] > 0 then - break - end - end - - turn(false) -end - -local chunkX, chunkZ, chunkRotation, chunkRadius, chunkRadiusCounter, chunkWorldX, chunkWorldZ = 0, 0, 0, 1, 1 -for chunk = 1, chunkCount do - chunkWorldX, chunkWorldZ = chunkX * 8, chunkZ * 8 - - --print("Пиздую к текущему чанку", chunkX, chunkZ) - moveTo(chunkWorldX, -1, chunkWorldZ) - - while true do - --print("Сканирую чанк", chunkX, chunkZ) - local scanX, scanZ, scanIndex, ores, scanResult, bedrock = - positionX, - positionZ, - 1, - {}, - geolyzer.scan( - chunkWorldX - positionX, - chunkWorldZ - positionZ, - -1, - 8, - 8, - 1 - ) - - for z = 0, 7 do - for x = 0, 7 do - if scanResult[scanIndex] >= minDensity and scanResult[scanIndex] <= maxDensity then - table.insert(ores, chunkWorldX + x) - table.insert(ores, chunkWorldZ + z) - elseif scanResult[scanIndex] < -0.4 then - bedrock = true - end - - scanIndex = scanIndex + 1 - end - end - - if #ores > 0 then - --print("Нашел вот стока руд", #ores) - while #ores > 0 do - local nearestIndex, nearestDistance, distance = 1, math.huge - for i = 1, #ores, 2 do - distance = math.sqrt((ores[i] - positionX) ^ 2 + (ores[i + 1] - positionZ) ^ 2) - if distance < nearestDistance then - nearestIndex, nearestDistance = i, distance - end - end - - --print("Пиздую к руде на точку", ores[nearestIndex], positionY, ores[nearestIndex + 1]) - moveTo(ores[nearestIndex], positionY, ores[nearestIndex + 1]) - robotSwing(0) - - for i = 1, 2 do - table.remove(ores, nearestIndex) - end - end - else - --print("Ни хуя тут руд нет") - end - - --print("Чекаем генератор") - if generator and generator.count() == 0 then - --print("Генератор пустой чота") - for i = 1, inventorySize do - robotSelect(i) - if generator.insert() then - --print("Генератор заправлен") - break - end - end - end - - --print("Чекаем инстурмент") - if robot.durability() <= 0.2 then - --print("Инструмент хуевый") - for i = 1, inventorySize do - local stack = inventory_controllerGetStackInInternalSlot(i) - if stack and checkItemInTable(tools, stack.name) and stack.damage / stack.maxDamage < 0.8 then - --print("Ща сменю его") - robotSelect(i) - inventory_controller.equip() - break - end - end - end - - --print("Чекаю фри слоты или энергию", freeSlots, getEnergy()) - local freeSlots = 0 - for i = 1, inventorySize do - if robot.count(i) == 0 then - freeSlots = freeSlots + 1 - end - end - - if freeSlots <= 1 then - --print("Фри слотов маловато, пытаюсь дропнуть треш") - freeSlots = freeSlots + drop(true) - end - - if freeSlots <= 3 or getEnergy() <= 0.2 then - --print("Чота все ваще хуева: либо слотов мало, либо энергии не хватает... заебало") - local oldX, oldY, oldZ, oldRotation = positionX, positionY, positionZ, rotation - moveToBase() - - if getEnergy() <= 0.2 then - --print("еще и зарядки мало, ща подзарядимсо") - while getEnergy() < 0.99 do - --print("Заряжаюсь", getEnergy()) - computer.pullSignal(1) - end - end - - --print("Пиздую назад") - moveTo(oldX, oldY, oldZ) - turnTo(oldRotation) - end - - if bedrock or positionY <= worldHeight then - --print("Бедрок чет нашел на Y или низковато опустился", positionY - 1) - break - else - --print("Руды выкопаны. Сдвигаюсь вниз и ебошу") - move(0) - end - end - - --print("Чанк полностью выкопан, рассчитываю коорды следующего") - chunkX, chunkZ = chunkX + (chunkRotation == 0 and 1 or chunkRotation == 2 and -1 or 0), chunkZ + (chunkRotation == 1 and 1 or chunkRotation == 3 and -1 or 0) - - if - (chunkRotation == 0 or chunkRotation == 2) and mathAbs(chunkX) >= chunkRadius or - (chunkRotation == 1 or chunkRotation == 3) and mathAbs(chunkZ) >= chunkRadius - then - chunkRadiusCounter = chunkRadiusCounter + 1 - if chunkRadiusCounter > 5 then - chunkRadius, chunkRadiusCounter = chunkRadius + 1, 1 - --print("Радиус увеличил", chunkRadius) - else - chunkRotation = chunkRotation + 1 - if chunkRotation > 3 then - chunkRotation = 0 - end - --print("Паварооот", chunkRotation) - end - end -end - -moveToBase() -turnTo(0) ---print("Усе, епта") \ No newline at end of file diff --git a/Applications/Robot/GeoMiner/Minified.lua b/Applications/Robot/GeoMiner/Minified.lua deleted file mode 100644 index 1348fb67..00000000 --- a/Applications/Robot/GeoMiner/Minified.lua +++ /dev/null @@ -1 +0,0 @@ -local a,b,c,d,e,f,g,h,i,j=2,10,9,-100,{cobblestone=1,sandstone=1,stone=1,dirt=1,gravel=1,hardened_clay=1,nether_brick=1,sand=1,soul_sand=1,netherrack=1},{diamond_pickaxe=1,iron_pickaxe=1},math.abs,function(k)k=component.list(k)()return k and component.proxy(k)or nil end,function()return computer.energy()/computer.maxEnergy()end,function(table,l)return table[l]or table[l:gsub("minecraft:","")]end;local m,n,o,p=h("robot"),h("geolyzer"),h("inventory_controller"),h("generator")local q,r,s,t,u,v,w,x,y=0,0,0,0,m.inventorySize(),m.swing,m.select,n.scan,o.getStackInInternalSlot;local z,A=function(B)m.turn(B)t=t+(B and 1 or-1)t=t>3 and 0 or t<0 and 3 or t end,function(C)while true do local D,E=v(C)if D or E=="air"then D,E=m.move(C)if D then if C==0 or C==1 then r=r+(C==1 and 1 or-1)else q,s=q+(t==0 and 1 or t==2 and-1 or 0),s+(t==1 and 1 or t==3 and-1 or 0)end;break end else if E=="block"then while true do computer.beep(1500,1)end end end end end;local function F(G)local H=t-G;if H~=0 then local I=H>2;if H<=0 then I=-H<=2 end;while t~=G do z(I)end end end;local J,K=function(L,M,N)L,M,N=L-q,M-r,N-s;if M~=0 then for O=1,g(M)do A(M>0 and 1 or 0)end end;if L~=0 then F(L>0 and 0 or 2)for O=1,g(L)do A(3)end end;if N~=0 then F(N>0 and 1 or 3)for O=1,g(N)do A(3)end end end,function(P)local Q=0;for O=1,u do local R=y(O)if R then local S=j(e,R.name)if P and S or not P and not j(f,R.name)then w(O)m.drop(S and 0 or 3)Q=Q+1 end end end;return Q end;local function T()J(0,r,0)J(0,0,0)for O=0,3 do local U=o.getInventorySize(3)if U and U>3 then K()return else z(true)end end;K(true)end;w(1)A(0)local V=x(1,0)[33]for O=0,3 do if V>0 then if v(3)and x(1,0)[33]==0 then break end else if m.place(3)and x(1,0)[33]>0 then break end end;z(false)end;local W,X,Y,Z,_,a0,a1=0,0,0,1,1;for a2=1,c do a0,a1=W*8,X*8;J(a0,-1,a1)while true do local a3,a4,a5,a6,a7,a8=q,s,1,{},n.scan(a0-q,a1-s,-1,8,8,1)for a9=0,7 do for aa=0,7 do if a7[a5]>=a and a7[a5]<=b then table.insert(a6,a0+aa)table.insert(a6,a1+a9)elseif a7[a5]<-0.4 then a8=true end;a5=a5+1 end end;if#a6>0 then while#a6>0 do local ab,ac,ad=1,math.huge;for O=1,#a6,2 do ad=math.sqrt((a6[O]-q)^2+(a6[O+1]-s)^2)if ad=Z or(Y==1 or Y==3)and g(X)>=Z then _=_+1;if _>5 then Z,_=Z+1,1 else Y=Y+1;if Y>3 then Y=0 end end end end;T()F(0) \ No newline at end of file diff --git a/Applications/Robot/ReactorFiller.lua b/Applications/Robot/ReactorFiller.lua deleted file mode 100644 index eabbb80a..00000000 --- a/Applications/Robot/ReactorFiller.lua +++ /dev/null @@ -1,87 +0,0 @@ - -local component = require("component") -local sides = require("sides") -local robot = require("robot") -local event = require("event") -local serialization = require("serialization") - -local inventory_controller = component.inventory_controller - -local QUAD_FUEL = "IC2:reactorMOXQuad" -local DIAM_VENT = "IC2:reactorVentDiamond" -local IRON_VENT = "IC2:reactorVentSpread" -local STON_VENT = "IC2:reactorVent" -local GOLD_VENT = "IC2:reactorHeatSwitchSpread" -local RCTR_PLAT = "IC2:reactorPlating" - -local allowed = { - [QUAD_FUEL] = true, - [DIAM_VENT] = true, - [IRON_VENT] = true, - [STON_VENT] = true, - [GOLD_VENT] = true, - [RCTR_PLAT] = true, -} - -local map = { - DIAM_VENT, GOLD_VENT, DIAM_VENT, IRON_VENT, RCTR_PLAT, IRON_VENT, DIAM_VENT, GOLD_VENT, DIAM_VENT, - IRON_VENT, DIAM_VENT, QUAD_FUEL, DIAM_VENT, IRON_VENT, DIAM_VENT, QUAD_FUEL, DIAM_VENT, IRON_VENT, - STON_VENT, IRON_VENT, DIAM_VENT, IRON_VENT, DIAM_VENT, IRON_VENT, DIAM_VENT, IRON_VENT, STON_VENT, - GOLD_VENT, DIAM_VENT, IRON_VENT, DIAM_VENT, QUAD_FUEL, DIAM_VENT, IRON_VENT, DIAM_VENT, GOLD_VENT, - DIAM_VENT, QUAD_FUEL, DIAM_VENT, IRON_VENT, DIAM_VENT, IRON_VENT, DIAM_VENT, QUAD_FUEL, DIAM_VENT, - IRON_VENT, DIAM_VENT, GOLD_VENT, STON_VENT, IRON_VENT, STON_VENT, GOLD_VENT, DIAM_VENT, IRON_VENT, -} - -local args = {...} - -if args[1] == "fill" then - print("Filling...") - - local filledMap = {} - for robotSlot = 1, robot.inventorySize() do - local stack = inventory_controller.getStackInInternalSlot(robotSlot) - if stack and allowed[stack.name] then - for mapSlot = 1, #map do - if map[mapSlot] == stack.name and not filledMap[mapSlot] then - robot.select(robotSlot) - print("Drop status", inventory_controller.dropIntoSlot(sides.front, mapSlot, 1)) - filledMap[mapSlot] = true - break - end - end - end - end -elseif args[1] == "count" then - print("Checking...") - - local need, have = {}, {} - for i = 1, #map do - need[map[i]] = (need[map[i]] or 0) + 1 - end - - for i = 1, robot.inventorySize() do - local stack = inventory_controller.getStackInInternalSlot(i) - if stack and allowed[stack.name] then - have[stack.name] = (have[stack.name] or 0) + stack.size - end - end - - for key, value in pairs(need) do - local name = key:sub(12, -1) - - if have[key] then - if have[key] < value then - print(name .. ": put " .. (value - (have[key] or 0))) - else - print(name .. ": OK, over " .. (have[key] - value)) - end - else - print(name .. ": put " .. value) - end - end -end - - - - - diff --git a/Applications/Robot/VRScanComputer.lua b/Applications/Robot/VRScanComputer.lua deleted file mode 100644 index 9773d50b..00000000 --- a/Applications/Robot/VRScanComputer.lua +++ /dev/null @@ -1,156 +0,0 @@ - -local component = require("component") -local GUI = require("GUI") -local color = require("color") -local computer = require("computer") -local filesystem = require("filesystem") -local MineOSPaths = require("MineOSPaths") - -local glasses = component.glasses -local modem = component.modem - --------------------------------------------------------------------------------- - -local port = 512 - -local config = { - glassesX = 0, - glassesY = 0, - glassesZ = 0, - robotX = 0, - robotY = 0, - robotZ = 0, - width = 3, - height = 1, - length = 3, - radius = 16, - minDensity = 0.2, - maxDensity = 10, -} - -local configPath = MineOSPaths.applicationData .. "VRScan4.cfg" -if filesystem.exists(configPath) then - config = table.fromFile(configPath) -end - --------------------------------------------------------------------------------- - -local function broadcast(...) - modem.broadcast(port, "VRScan", ...) -end - -local mainContainer = GUI.fullScreenContainer() -mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0xF0F0F0)) - -local layout = mainContainer:addChild(GUI.layout(1, 1, mainContainer.width, mainContainer.height, 1, 1)) - -local width = 39 - -local function addCoords(v1, v2, v3, t1, t2, t3) - local subLayout = layout:addChild(GUI.layout(1, 1, width, 3, 1, 1)) - subLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL) - subLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP) - - local width = math.floor((width - 2) / 3) - return - subLayout:addChild(GUI.input(1, 1, width, 3, 0xE1E1E1, 0x787878, 0xB4B4B4, 0xE1E1E1, 0x2D2D2D, v1, t1)), - subLayout:addChild(GUI.input(1, 1, width, 3, 0xE1E1E1, 0x787878, 0xB4B4B4, 0xE1E1E1, 0x2D2D2D, v2, t2)), - subLayout:addChild(GUI.input(1, 1, width, 3, 0xE1E1E1, 0x787878, 0xB4B4B4, 0xE1E1E1, 0x2D2D2D, v3, t3)) -end - -local glassesXInput, glassesYInput, glassesZInput = addCoords(tostring(config.glassesX), tostring(config.glassesY), tostring(config.glassesZ), "Glasses X", "Glasses Y", "Glasses Z") - -local robotXInput, robotYInput, robotZInput = addCoords(tostring(config.robotX), tostring(config.robotY), tostring(config.robotZ), "Robot X", "Robot Y", "Robot Z") - -local widthSlider = layout:addChild(GUI.slider(1, 1, width, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xB4B4B4, 1, 10, config.width, false, "Width: ", "")) -widthSlider.height = 2 -widthSlider.roundValues = true - -local heightSlider = layout:addChild(GUI.slider(1, 1, width, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xB4B4B4, 1, 10, config.height, false, "Height: ", "")) -heightSlider.height = 2 -heightSlider.roundValues = true - -local lengthSlider = layout:addChild(GUI.slider(1, 1, width, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xB4B4B4, 1, 10, config.length, false, "Length: ", "")) -lengthSlider.height = 2 -lengthSlider.roundValues = true - -local radiusSlider = layout:addChild(GUI.slider(1, 1, width, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xB4B4B4, 1, 24, config.radius, false, "Radius: ", "")) -radiusSlider.height = 2 -radiusSlider.roundValues = true - -local minDensitySlider = layout:addChild(GUI.slider(1, 1, width, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xB4B4B4, 0, 10, config.minDensity, false, "Min density: ", "")) -minDensitySlider.height = 2 - -local maxDensitySlider = layout:addChild(GUI.slider(1, 1, width, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xB4B4B4, 0, 10, config.maxDensity, false, "Max density: ", "")) -maxDensitySlider.height = 2 - -local function saveConfig() - config.glassesX = tonumber(glassesXInput.text) - config.glassesY = tonumber(glassesYInput.text) - config.glassesZ = tonumber(glassesZInput.text) - config.robotX = tonumber(robotXInput.text) - config.robotY = tonumber(robotYInput.text) - config.robotZ = tonumber(robotZInput.text) - config.width = math.floor(widthSlider.value) - config.height = math.floor(heightSlider.value) - config.length = math.floor(lengthSlider.value) - config.radius = math.floor(radiusSlider.value) - config.minDensity = minDensitySlider.value - config.maxDensity = maxDensitySlider.value - - table.toFile(configPath, config) -end - -layout:addChild(GUI.button(1, 1, width, 3, 0xC3C3C3, 0xFFFFFF, 0x969696, 0xFFFFFF, "Scan")).onTouch = function() - saveConfig() - - broadcast("scan", table.toString({ - width = config.width, - height = config.height, - length = config.length, - radius = config.radius, - minDensity = config.minDensity, - maxDensity = config.maxDensity - })) - - glasses.removeAll() -end - -layout.eventHandler = function(mainContainer, layout, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12) - if e1 == "modem_message" and e6 == "VRScan" then - if e7 == "result" then - local result = table.fromString(e8) - - for x in pairs(result.blocks) do - for y in pairs(result.blocks[x]) do - for z in pairs(result.blocks[x][y]) do - for i = 1, #result.blocks[x][y][z] do - local cube = glasses.addCube3D() - cube.setVisibleThroughObjects(true) - - local maxHue = 240 - local hue = (1 - result.blocks[x][y][z][i] / config.maxDensity) * maxHue - local r, g, b = color.HSBToRGB(hue, 1, 1) - - cube.setColor(r / 255, g / 255, b / 255) - cube.setAlpha(0.5) - cube.set3DPos( - config.robotX - config.glassesX + result.x + x, - config.robotY - config.glassesY + result.y + y, - config.robotZ - config.glassesZ + result.z + z - ) - end - - computer.pullSignal(0) - end - end - end - end - end -end - --------------------------------------------------------------------------------- - -modem.open(port) -mainContainer:drawOnScreen(true) -mainContainer:startEventHandling() \ No newline at end of file diff --git a/Applications/Robot/VRScanRobot.lua b/Applications/Robot/VRScanRobot.lua deleted file mode 100644 index d5144f67..00000000 --- a/Applications/Robot/VRScanRobot.lua +++ /dev/null @@ -1,133 +0,0 @@ - -local AR = require("advancedRobot") -local event = require("event") -local sides = require("sides") -local serialization = require("serialization") - -local port = 512 -AR.proxies.modem.open(port) - --------------------------------------------------------------------------------- - -local function broadcast(...) - AR.proxies.modem.broadcast(port, "VRScan", ...) -end - -local function round(num, decimalPlaces) - local mult = 10 ^ (decimalPlaces or 0) - return math.floor(num * mult + 0.5) / mult -end - -local function move(x, y, z) - print("Moving to next chunk with offset " .. x .. "x" .. y .. "x" .. z) - AR.moveToPosition(AR.positionX + x, AR.positionY + y, AR.positionZ + z) -end - -print("Waiting for modem commands...") - -while true do - local e = {event.pull()} - if e[1] == "modem_message" and e[6] == "VRScan" then - if e[7] == "scan" then - local settings = serialization.unserialize(e[8]) - local w, h, l - - AR.positionX = 0 - AR.positionY = 0 - AR.positionZ = 0 - AR.rotation = 0 - - print("Scanning with paramerers: " .. e[8]) - - local function moveChunk(side) - for i = 1, settings.radius * 2 + 1 do - AR.swingAndMove(side) - end - end - - local function doChunk() - print("Scanning chunk " .. w .. " x " .. h .. " x " .. l) - - local result, column = { - x = AR.positionX, - y = AR.positionY, - z = AR.positionZ, - blocks = {} - } - - local blockCount = 0 - for z = -settings.radius, settings.radius do - for x = -settings.radius, settings.radius do - column = AR.proxies.geolyzer.scan(x, z) - - for i = 32 - settings.radius, 32 + settings.radius do - if column[i] >= settings.minDensity and column[i] <= settings.maxDensity then - local y = i - 33 - result.blocks[x] = result.blocks[x] or {} - result.blocks[x][y] = result.blocks[x][y] or {} - result.blocks[x][y][z] = result.blocks[x][y][z] or {} - table.insert(result.blocks[x][y][z], round(column[i], 2)) - - blockCount = blockCount + 1 - end - end - end - end - - print("Scanning finished. Sending result with blocks size: " .. blockCount) - broadcast("result", serialization.serialize(result)) - end - - local function doLength() - l = 1 - while l <= settings.length do - doChunk() - if l < settings.length then - moveChunk(sides.front) - end - l = l + 1 - end - end - - h = 1 - while h <= settings.height do - w = 1 - while w <= settings.width do - doLength() - - AR.turnRight() - moveChunk(sides.front) - AR.turnRight() - - doLength() - - if w < settings.width then - AR.turnLeft() - moveChunk(sides.front) - AR.turnLeft() - else - AR.turnRight() - for i = 1, settings.width * 2 - 1 do - moveChunk(sides.front) - end - AR.turnRight() - - if h < settings.height then - moveChunk(sides.up) - else - for i = 1, settings.height - 1 do - moveChunk(sides.down) - end - end - end - - w = w + 1 - end - - h = h + 1 - end - - print("Task finished") - end - end -end diff --git a/Applications/Robot/advancedRobot.lua b/Applications/Robot/advancedRobot.lua deleted file mode 100644 index 1709eb05..00000000 --- a/Applications/Robot/advancedRobot.lua +++ /dev/null @@ -1,400 +0,0 @@ - -local component = require("component") -local computer = require("computer") -local event = require("event") -local sides = require("sides") - -local AR = {} - --------------------------------------------------------------------------------- - -AR.proxies = {} -AR.requiredProxies = { - ["robot"] = true, - ["generator"] = true, - ["inventory_controller"] = true, - ["modem"] = true, - ["geolyzer"] = true, - ["redstone"] = true, - ["experience"] = true, - ["chunkloader"] = true, -} - -AR.fuels = { - ["minecraft:coal"] = true, - ["minecraft:coal_block"] = true, - ["minecraft:lava_bucket"] = true, - ["minecraft:coal_block"] = true, -} - -AR.droppables = { - ["minecraft:cobblestone"] = true, - ["minecraft:stone"] = true, - ["minecraft:grass"] = true, - ["minecraft:dirt"] = true, - ["minecraft:gravel"] = true, - ["minecraft:sand"] = true, - ["minecraft:sandstone"] = true, - ["minecraft:torch"] = true, - ["minecraft:planks"] = true, - ["minecraft:fence"] = true, - ["minecraft:chest"] = true, - ["minecraft:monster_egg"] = true, - ["minecraft:stonebrick"] = true, -} - -AR.tools = { - ["minecraft:diamond_pickaxe"] = true, - ["minecraft:iron_pickaxe"] = true, -} - -AR.positionX = 0 -AR.positionY = 0 -AR.positionZ = 0 -AR.rotation = 0 - --------------------------------------------------------------------------------- - -function AR.rotatePosition(x, y, z) - if AR.rotation == 0 then - return x, y, z - elseif AR.rotation == 1 then - return z, y, -x - elseif AR.rotation == 2 then - return -x, y, -z - else - return -z, y, x - end -end - -function AR.reset() - AR.positionX, AR.positionY, AR.positionZ, AR.rotation = 0, 0, 0, 0 -end - -function AR.updateProxies() - local name - for name in pairs(AR.requiredProxies) do - AR.proxies[name] = component.list(name)() - if AR.proxies[name] then - AR.proxies[name] = component.proxy(AR.proxies[name]) - end - end -end - -AR.reset() -AR.updateProxies() - -for key, value in pairs(AR.proxies.robot) do - AR[key] = value -end - --------------------------------------------------------------------------------- - -function AR.move(direction) - local success, reason = AR.proxies.robot.move(direction) - - if success then - if direction == sides.front or direction == sides.back then - local directionOffset = direction == sides.front and 1 or -1 - - if AR.rotation == 0 then - AR.positionX = AR.positionX + directionOffset - elseif AR.rotation == 1 then - AR.positionZ = AR.positionZ + directionOffset - elseif AR.rotation == 2 then - AR.positionX = AR.positionX - directionOffset - elseif AR.rotation == 3 then - AR.positionZ = AR.positionZ - directionOffset - end - elseif direction == sides.up or direction == sides.down then - AR.positionY = AR.positionY + (direction == sides.up and 1 or -1) - end - end - - return success, reason -end - -function AR.turn(clockwise) - AR.proxies.robot.turn(clockwise) - AR.rotation = AR.rotation + (clockwise and 1 or -1) - - if AR.rotation > 3 then - AR.rotation = 0 - elseif AR.rotation < 0 then - AR.rotation = 3 - end -end - -function AR.turnToRotation(requiredRotation) - local difference = AR.rotation - requiredRotation - if difference ~= 0 then - local fastestWay - if difference > 0 then - fastestWay = difference > 2 - else - fastestWay = -difference <= 2 - end - - while AR.rotation ~= requiredRotation do - AR.turn(fastestWay) - end - end -end - -function AR.moveToPosition(xTarget, yTarget, zTarget) - local xDistance, yDistance, zDistance = xTarget - AR.positionX, yTarget - AR.positionY, zTarget - AR.positionZ - - if yDistance ~= 0 then - for i = 1, math.abs(yDistance) do - AR.swingAndMove(yDistance > 0 and sides.up or sides.down) - end - end - - if xDistance ~= 0 then - AR.turnToRotation(xDistance > 0 and 0 or 2) - for i = 1, math.abs(xDistance) do - AR.swingAndMove(sides.front) - end - end - - if zDistance ~= 0 then - AR.turnToRotation(zDistance > 0 and 1 or 3) - for i = 1, math.abs(zDistance) do - AR.swingAndMove(sides.front) - end - end -end - --------------------------------------------------------------------------------- - --- Swing -function AR.swingForward() - return AR.proxies.robot.swing(sides.front) -end - -function AR.swingUp() - return AR.proxies.robot.swing(sides.up) -end - -function AR.swingDown() - return AR.proxies.robot.swing(sides.down) -end ---Use -function AR.useForward() - return AR.proxies.robot.use(sides.front) -end - -function AR.useUp() - return AR.proxies.robot.use(sides.up) -end - -function AR.useDown() - return AR.proxies.robot.use(sides.down) -end --- Move -function AR.moveForward() - return AR.move(sides.front) -end - -function AR.moveBackward() - return AR.move(sides.back) -end - -function AR.moveUp() - return AR.move(sides.up) -end - -function AR.moveDown() - return AR.move(sides.down) -end --- Turn -function AR.turnLeft() - return AR.turn(false) -end - -function AR.turnRight() - return AR.turn(true) -end - -function AR.turnAround() - AR.turn(true) - AR.turn(true) -end - --------------------------------------------------------------------------------- - -local function callProxyMethod(proxyName, methodName, ...) - if AR.proxies[proxyName] then - return AR.proxies[proxyName][methodName](...) - else - return false, proxyName .. " component is not available" - end -end - -function AR.equip(...) - return callProxyMethod("inventory_controller", "equip", ...) -end - -function AR.getStackInInternalSlot(...) - return callProxyMethod("inventory_controller", "getStackInInternalSlot", ...) -end - --------------------------------------------------------------------------------- - -function AR.moveToZeroPosition(noTurn) - AR.moveToPosition(0, AR.positionY, 0) - AR.turnToRotation(0) - AR.moveToPosition(0, 0, 0) -end - -function AR.swingAndMove(direction) - while true do - local swingSuccess, swingReason = AR.swing(direction) - if swingSuccess or swingReason == "air" then - local moveSuccess, moveReason = AR.move(direction) - if moveSuccess then - return moveSuccess, moveReason - end - else - if swingReason == "block" then - AR.moveToZeroPosition() - return false, "Unbreakable block detected, going to base" - end - end - end -end - -function AR.getEmptySlotsCount() - local count = 0 - for slot = 1, AR.inventorySize() do - if AR.count(slot) == 0 then - count = count + 1 - end - end - - return count -end - -function AR.dropDroppables(side) - local oldSlot, need = AR.select() - for slot = 1, AR.inventorySize() do - local stack = AR.getStackInInternalSlot(slot) - if stack then - if AR.droppables[stack.name] then - AR.select(slot) - AR.drop(side or sides.down) - need = true - end - end - end - - if need then - AR.select(oldSlot) - end -end - -function AR.dropAll(side, exceptArray) - exceptArray = exceptArray or AR.tools - local oldSlot = AR.select() - - for slot = 1, AR.inventorySize() do - local stack = AR.getStackInInternalSlot(slot) - if stack then - if not exceptArray[stack.name] then - AR.select(slot) - AR.drop(side) - end - end - end - - AR.select(oldSlot) -end - -function AR.checkToolStatus(percent) - if AR.durability() < percent then - print("Equipped tool durability lesser then " .. percent) - - for slot = 1, AR.inventorySize() do - local stack = AR.getStackInInternalSlot(slot) - if stack then - if stack.damage / stack.maxDamage < percent and AR.tools[stack.name] then - local oldSlot = AR.select() - AR.select(slot) - AR.equip() - AR.select(oldSlot) - print("Tool switched to " .. stack.label) - - break - end - end - end - end -end - -function AR.charge(percent) - while computer.energy() / computer.maxEnergy() < percent do - print("Charging up: " .. math.floor(computer.energy() / computer.maxEnergy() * 100) .. "%") - os.sleep(1) - end -end - -function AR.getSlotWithFuel() - for slot = 1, AR.inventorySize() do - local stack = AR.getStackInInternalSlot(slot) - if stack then - if AR.fuels[stack.name] then - return slot - end - end - end -end - -function AR.checkGeneratorStatus(count) - if AR.proxies.generator then - if AR.proxies.generator.count() == 0 then - print("Generator is empty, trying to find some fuel in inventory") - local slot = AR.getSlotWithFuel() - if slot then - print("Found slot with fuel: " .. slot) - local oldSlot = AR.select() - AR.select(slot) - AR.proxies.generator.insert(count) - AR.select(oldSlot) - return - else - print("Slot with fuel not found, skipping") - end - end - end -end - -function AR.getWorldRotation() - local initial = AR.proxies.geolyzer.scan(1, 0)[33] - for i = 0, 3 do - if initial > 0 then - if AR.swing(3) and AR.proxies.geolyzer.scan(1, 0)[33] == 0 then - for j = 1, i do - AR.turn(true) - end - - return i - end - else - if AR.place(3) and AR.proxies.geolyzer.scan(1, 0)[33] > 0 then - for j = 1, i do - AR.swing(3) - AR.turn(true) - end - AR.swing(3) - - return i - end - end - - AR.turn(false) - end -end - --------------------------------------------------------------------------------- - -return AR \ No newline at end of file diff --git a/Applications/Robot/commander.lua b/Applications/Robot/commander.lua deleted file mode 100644 index a96efd61..00000000 --- a/Applications/Robot/commander.lua +++ /dev/null @@ -1,55 +0,0 @@ -local AR = require("advancedRobot") -local args = {...} - -if #args == 0 then - print("Usage:") - print(" commander ") - return -end - -print("Task stated") - -local function execute(symbol) - if symbol == "f" then - print("Moving forward:", AR.moveForward()) - elseif symbol == "b" then - print("Moving backward:", AR.moveBackward()) - elseif symbol == "u" then - print("Moving up:", AR.moveUp()) - elseif symbol == "d" then - print("Moving down:", AR.moveDown()) - elseif symbol == "r" then - print("Turning right:", AR.turnRight()) - elseif symbol == "l" then - print("Turning left:", AR.turnLeft()) - elseif symbol == "t" then - print("Turning around:", AR.turnAround()) - elseif symbol == "s" then - print("Swinging:", AR.swingForward()) - elseif symbol == "e" then - print("Swinging:", AR.useForward()) - elseif symbol == "." then - print("Returning to start:", AR.moveToZeroPosition()) - end -end - -local i, commands, symbol, starting, ending, count = 1, args[1] -while i <= #commands do - starting, ending = commands:find("%d+", i) - - if starting == i then - symbol, count = commands:sub(ending + 1, ending + 1), tonumber(commands:sub(starting, ending)) - - print("Executing \"" .. symbol .. "\" command for " .. count .. " times") - for j = 1, count do - execute(symbol) - end - - i = ending + 2 - else - execute(commands:sub(i, i)) - i = i + 1 - end -end - -print("Task finished") diff --git a/Applications/Robot/download.lua b/Applications/Robot/download.lua deleted file mode 100644 index d8363402..00000000 --- a/Applications/Robot/download.lua +++ /dev/null @@ -1,24 +0,0 @@ -local shell = require("shell") - -local list = { - { - url = "https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Robot/advancedRobot.lua", - path = "/lib/advancedRobot.lua", - }, - { - url = "https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Robot/commander.lua", - path = "commander.lua", - }, - { - url = "https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Robot/experience.lua", - path = "experience.lua", - }, - { - url = "https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Robot/VRScanRobot.lua", - path = "VRScan.lua", - }, -} - -for i = 1, #list do - shell.execute("wget " .. list[i].url .. " " .. list[i].path .. " -f") -end \ No newline at end of file diff --git a/Applications/Robot/experience.lua b/Applications/Robot/experience.lua deleted file mode 100644 index 843856cb..00000000 --- a/Applications/Robot/experience.lua +++ /dev/null @@ -1,46 +0,0 @@ -local component = require("component") -local robot = require("robot") - -local args = {...} - -local function printUsage() - print("Usages:") - print("exp") - print(" Gets the current level.") - print("exp ") - print(" Tries to consume an enchanted item to add") - print(" expierence to the upgrade") - print(" from the specified slot.") - print("exp all") - print(" from all slots.") -end - -if component.isAvailable("experience") then - local e = component.experience - if #args == 0 then - print("Level: "..e.level()) - elseif tonumber(args[1]) ~= nil then - local slot = tonumber(args[1]) - robot.select(slot) - io.write("Experience from slot "..slot.."... ") - local success, msg = e.consume() - if success then - print("success.") - else - print("failed: "..msg) - end - robot.select(1) - elseif string.lower(args[1]) == "all" then - io.write("Experience from all slots... ") - for i = 1, robot.inventorySize() do - robot.select(i) - e.consume() - end - robot.select(1) - print("done.") - else - printUsage() - end -else - print("This program requires the experience upgrade to be installed.") -end diff --git a/Applications/Robot/fisherEEPROM.lua b/Applications/Robot/fisherEEPROM.lua deleted file mode 100644 index aac9c32f..00000000 --- a/Applications/Robot/fisherEEPROM.lua +++ /dev/null @@ -1,63 +0,0 @@ -local robot = component.proxy(component.list("robot")()) -local redstone = component.proxy(component.list("redstone")()) -local inventory_controller = component.list("inventory_controller")() -if inventory_controller then - inventory_controller = component.proxy(inventory_controller) -else - error("No inventory_controller") -end - -local minDurability = 0.1 -local tryCatchTime = 30 -local startSleepTime = 2 -local catchSleepTime = 0.5 -local dropSide = 0 -local pullSide = 3 -local rodStorageSide = 1 - -local function sleep(timeout) - local deadline = computer.uptime() + timeout - while computer.uptime() < deadline do - computer.pullSignal(deadline - computer.uptime()) - end -end - -local function push() - robot.use(pullSide) - sleep(startSleepTime) -end - -local function tool() - for i = 2, 1, -1 do - robot.select(i) - robot.drop(dropSide) - end - - local durability = robot.durability() - if not durability or durability <= minDurability then - robot.suck(rodStorageSide) - inventory_controller.equip() - end -end - -local function pull() - robot.use(pullSide) - sleep(catchSleepTime) - - tool() - push() -end - -tool() -push() - -while true do - local e = {computer.pullSignal(tryCatchTime)} - if e[1] == "redstone_changed" then - if e[5] == 0 then - pull() - end - elseif not e[1] then - pull() - end -end \ No newline at end of file diff --git a/Applications/RobotGriefer/Receiver.lua b/Applications/RobotGriefer/Receiver.lua deleted file mode 100644 index 3edcbcbf..00000000 --- a/Applications/RobotGriefer/Receiver.lua +++ /dev/null @@ -1,134 +0,0 @@ - -local component = require("component") -local robot = require("robot") -local event = require("event") -local fs = require("filesystem") -local port = 512 -local keyWord = "ECSGrief" -local modem -local redstone = component.redstone -local redstoneState = false -local toolUsingMode = false -local toolUsingSide = 1 - -if component.isAvailable("modem") then - modem = component.modem -else - error("Этой программе требуется беспроводной модем для работы!") -end - -modem.open(port) - -------------------------------------------------------------------------------------- - -local commands = { - forward = robot.forward, - back = robot.back, - turnRight = robot.turnRight, - turnLeft = robot.turnLeft, - up = robot.up, - down = robot.down, - swing = robot.swing, - drop = robot.drop -} - -local function redstoneControl() - if not redstone then return end - if redstoneState then - for i = 0, 5 do - redstone.setOutput(i, 0) - end - print("Сигнал редстоуна включен со всех сторон робота!") - redstoneState = false - else - for i = 0, 5 do - redstone.setOutput(i, 15) - end - print("Сигнал редстоуна отключен.") - redstoneState = true - end -end - -local function receive() - while true do - local eventData = { event.pull() } - if eventData[1] == "modem_message" and eventData[4] == port and eventData[6] == keyWord then - local message = eventData[7] - local message2 = eventData[8] - - if commands[message] then - commands[message]() - else - if message == "selfDestroy" then - local fs = require("filesystem") - for file in fs.list("") do - print("Уничтожаю \"" .. file .. "\"") - fs.remove(file) - end - require("term").clear() - require("computer").shutdown() - elseif message == "use" then - if toolUsingMode then - if toolUsingSide == 1 then - print("Использую экипированный предмет в режиме правого клика перед роботом") - robot.use() - elseif toolUsingSide == 0 then - print("Использую экипированный предмет в режиме правого клика под роботом") - robot.useDown() - elseif toolUsingSide == 2 then - print("Использую экипированный предмет в режиме правого клика над роботом") - robot.useUp() - end - else - if toolUsingSide == 1 then - print("Использую экипированный предмет в режиме левого клика перед роботом") - robot.swing() - elseif toolUsingSide == 0 then - print("Использую экипированный предмет в режиме левого клика под роботом") - robot.swingDown() - elseif toolUsingSide == 2 then - print("Использую экипированный предмет в режиме левого клика над роботом") - robot.swingUp() - end - end - elseif message == "exit" then - return - elseif message == "redstone" then - redstoneControl() - elseif message == "changeToolUsingMode" then - toolUsingMode = not toolUsingMode - elseif message == "increaseToolUsingSide" then - print("Изменяю режим использования вещи") - toolUsingSide = toolUsingSide + 1 - if toolUsingSide > 2 then toolUsingSide = 2 end - elseif message == "decreaseToolUsingSide" then - print("Изменяю режим использования вещи") - toolUsingSide = toolUsingSide - 1 - if toolUsingSide < 0 then toolUsingSide = 0 end - end - end - end - end -end - -local function main() - print(" ") - print("Добро пожаловать в программу ECSGrief Receiver v1.0 alpha early access! Идет ожидание команд с беспроводного устройства.") - print(" ") - receive() - print(" ") - print("Программа приема сообщений завершена!") -end - -------------------------------------------------------------------------------------- - -main() - -------------------------------------------------------------------------------------- - - - - - - - diff --git a/Applications/RobotGriefer/Sender.lua b/Applications/RobotGriefer/Sender.lua deleted file mode 100644 index 4318203b..00000000 --- a/Applications/RobotGriefer/Sender.lua +++ /dev/null @@ -1,114 +0,0 @@ - -local component = require("component") -local event = require("event") -local port = 512 -local keyWord = "ECSGrief" -local modem - -if component.isAvailable("modem") then - modem = component.modem -else - error("Этой программе требуется беспроводной модем для работы!") -end - -modem.open(port) - -------------------------------------------------------------------------------------- - -local commands = { - [17] = { - messageToRobot = "forward", - screenText = "Приказываю роботу двигаться вперед", - }, - [31] = { - messageToRobot = "back", - screenText = "Приказываю роботу двигаться назад", - }, - [30] = { - messageToRobot = "turnLeft", - screenText = "Приказываю роботу повернуться налево", - }, - [32] = { - messageToRobot = "turnRight", - screenText = "Приказываю роботу повернуться направо", - }, - [57] = { - messageToRobot = "up", - screenText = "Приказываю роботу двигаться вверх", - }, - [42] = { - messageToRobot = "down", - screenText = "Приказываю роботу двигаться вниз", - }, - [18] = { - messageToRobot = "use", - screenText = "Приказываю роботу использовать предмет в руках", - }, - [14] = { - messageToRobot = "exit", - screenText = "Приказываю роботу завершить программу принятия сообщений", - }, - [59] = { - messageToRobot = "selfDestroy", - screenText = "Приказываю роботу уничтожить всю информацию на диске. Ему было приятно работать с тобой, повелитель!", - }, - [19] = { - messageToRobot = "redstone", - screenText = "Приказываю роботу включить/выключить редстоун вокруг себя", - }, - [16] = { - messageToRobot = "drop", - screenText = "Приказываю роботу выкинуть предмет из выбранного слота", - }, - [33] = { - messageToRobot = "changeToolUsingMode", - screenText = "Приказываю роботу изменить режим использования предмета, а именно swing() или use()", - }, -} - -local function send() - while true do - local eventData = { event.pull() } - if eventData[1] == "key_down" then - if commands[eventData[4]] then - print(commands[eventData[4]].screenText) - modem.broadcast(port, keyWord, commands[eventData[4]].messageToRobot) - if commands[eventData[4]].messageToRobot == "exit" then - return - end - end - elseif eventData[1] == "scroll" then - if eventData[5] == 1 then - print("Приказываю роботу увеличить режим использования предметов, т.е. useDown() изменится на use(), а use() на useUp()") - modem.broadcast(port, keyWord, "increaseToolUsingSide") - else - print("Приказываю роботу уменьшить режим использования предметов, т.е. useUp() изменится на use(), а use() на useDown()") - modem.broadcast(port, keyWord, "decreaseToolUsingSide") - end - end - end -end - -local function main() - print(" ") - print("Добро пожаловать в программу ECSGrief Sender v1.0 alpha early access!") - print(" ") - print("Используйте WASD, а также SPACE и SHIFT для перемещения. Нажатие клавиши E заставит робота использовать предмет, находящийся у него в руках. Также вы можете использовать клавишу F1 для экстренного удаления всех данных с робота и BACKSPACE для простого выхода из программы. Удачной охоты за ресами!") - print(" ") - send() - print(" ") - print("Программа доминации над роботом завершена!") -end - -------------------------------------------------------------------------------------- - -main() - -------------------------------------------------------------------------------------- - - - - - - - diff --git a/Applications/RobotGriefer/untitled.lua b/Applications/RobotGriefer/untitled.lua deleted file mode 100644 index fa7352bf..00000000 --- a/Applications/RobotGriefer/untitled.lua +++ /dev/null @@ -1,42 +0,0 @@ - -local component = require("component") -local event = require("event") -local ecs = require("ECSAPI") -local hologram = component.hologram -local printer = component.printer3d -local gpu = component.gpu - ------------------------------------------------------------------------------------------------------------------------- - -local colors = { - drawingZoneBackground = 0x262626, - toolbarBackground = 0x444444, -} - -local xSize, ySize = gpu.getResolution() -local widthOfToolbar = 2 -local xToolbar = xSize - widthOfToolbar -local widthOfDrawingZone = xSize - widthOfToolbar - ------------------------------------------------------------------------------------------------------------------------- - -local function drawDrawingZone() - ecs.square(1, 1, xSize, ySize, colors.drawingZoneBackground) -end - -local function drawToolbar() - ecs.square(xToolbar, 1, widthOfToolbar, ySize, colors.toolbarBackground) -end - -local function drawAll() - drawDrawingZone() - drawToolbar() -end - ------------------------------------------------------------------------------------------------------------------------- - -drawAll() - - - - diff --git a/Applications/RunningString/Icon.pic b/Applications/Running String.app/Icon.pic old mode 100755 new mode 100644 similarity index 100% rename from Applications/RunningString/Icon.pic rename to Applications/Running String.app/Icon.pic diff --git a/Applications/RunningString/RunningString.lua b/Applications/Running String.app/Main.lua similarity index 70% rename from Applications/RunningString/RunningString.lua rename to Applications/Running String.app/Main.lua index 6d640bdc..fa3f005e 100644 --- a/Applications/RunningString/RunningString.lua +++ b/Applications/Running String.app/Main.lua @@ -1,13 +1,14 @@ -local MineOSInterface = require("MineOSInterface") + +local system = require("System") local GUI = require("GUI") -local buffer = require("doubleBuffering") -local scale = require("scale") -local unicode = require("unicode") -local event = require("event") +local screen = require("Screen") +local event = require("Event") +local number = require("Number") --------------------------------------------------------------------------------------------------------- -local container = MineOSInterface.addBackgroundContainer(MineOSInterface.application, "Running string setup") +local workspace = system.getWorkspace() +local container = GUI.addBackgroundContainer(workspace, true, true, "Running string setup") local textInput = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xFFFFFF, 0x696969, 0xB4B4B4, 0xFFFFFF, 0x2D2D2D, "Working on cool things, don't distract me", "Type text here", true)) local backgroundColorSelector = container.layout:addChild(GUI.colorSelector(1, 1, 36, 3, 0x0, "Background color")) @@ -19,12 +20,15 @@ scaleSlider.roundValues, delaySlider.roundValues, spacingSlider.roundValues = tr spacingSlider.height = 2 container.layout:addChild(GUI.button(1, 1, 36, 3, 0x444444, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, "OK")).onTouch = function() - local text = textInput.text .. string.rep(" ", math.round(spacingSlider.value)) - local gpu = buffer.getGPUProxy() + local text = textInput.text .. string.rep(" ", number.round(spacingSlider.value)) + local gpu = screen.getGPUProxy() - scale.set(scaleSlider.value / 1000) - local width, height = gpu.getResolution() - local y = math.round(height / 2) + + local oldWidth, oldHeight = screen.getResolution() + local width, height = screen.getScaledResolution(scaleSlider.value / 1000) + local y = number.round(height / 2) + + screen.setResolution(width, height) gpu.setBackground(backgroundColorSelector.color) gpu.setForeground(textColorSelector.color) @@ -40,17 +44,8 @@ container.layout:addChild(GUI.button(1, 1, 36, 3, 0x444444, 0xFFFFFF, 0x2D2D2D, gpu.set(1, y, text) end - buffer.setResolution(buffer.getResolution()) - MineOSInterface.application:draw() + screen.setResolution(oldWidth, oldHeight) + container:remove() + + workspace:draw() end - - - - - - - - - - - diff --git a/Applications/RunningString/About/English.txt b/Applications/RunningString/About/English.txt deleted file mode 100644 index 96d61478..00000000 --- a/Applications/RunningString/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Бегущая строка, красиво и наглядно отображающая любое количество информации. Сделана специально по заказу Никиты Ярычева, ссылка на вк: http://vk.com/mcmodder. В общем, если ты читаешь это, то передай ему, что он пидор, ибо мне пришлось добавлять кучу новых фич в библиотеку ecsapi, а это нервы, нервы! \ No newline at end of file diff --git a/Applications/RunningString/About/Russian.txt b/Applications/RunningString/About/Russian.txt deleted file mode 100644 index 96d61478..00000000 --- a/Applications/RunningString/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Бегущая строка, красиво и наглядно отображающая любое количество информации. Сделана специально по заказу Никиты Ярычева, ссылка на вк: http://vk.com/mcmodder. В общем, если ты читаешь это, то передай ему, что он пидор, ибо мне пришлось добавлять кучу новых фич в библиотеку ecsapi, а это нервы, нервы! \ No newline at end of file diff --git a/Applications/Sample.app/Icon.pic b/Applications/Sample.app/Icon.pic new file mode 100644 index 0000000000000000000000000000000000000000..654082b8ec30cea55f451eccc0e4635136d94518 GIT binary patch literal 130 zcmXYp%L&6U5JY!oHNR}o8>vbLNflu{If#P zfC#ERLiBR 1 and redstones[comboBox.selectedItem - 1].address or nil - table.toFile(databasePath, database) - end - - updateButton() - - if comboBox.selectedItem > 1 and getRedstoneProxy().getOutput(1) > 0 then - setButtonState(object.button, true) - end - - return object -end - -local x, y, xStart, yStart = 3, 2, 3, 2 -for address in component.list("reactor_chamber") do - local proxy = component.proxy(address) - - local object = contorollersContainer:addChild(newController(x, y, proxy)) - - y = y + object.height + 1 - if y >= contorollersContainer.height - object.height then - x, y = x + object.width + 2, yStart - end -end - -local function update() - totalEnergy = 0 - - for i = 1, #contorollersContainer.children do - local object = contorollersContainer.children[i] - - local heat = object.reactorProxy.getHeat() - local value = heat / object.reactorProxy.getMaxHeat() - object.temperatureBar.value = value * 100 - object.temperatureBar.colors.active = palette[math.floor(value * #palette)] or palette[1] - object.temperatureLabel.text = "TEMP: " .. math.floor(heat) .. " °C" - object.energyLabel.text = "ENRG: " .. math.floor(object.reactorProxy.getReactorEUOutput()) .. " eU/t" - - totalEnergy = totalEnergy + math.floor(object.reactorProxy.getReactorEUOutput()) - end -end - -local uptime = computer.uptime() -contorollersContainer.eventHandler = function(mainContainer, object, e1) - if not e1 and computer.uptime() - uptime > delaySlider.value / 1000 then - update() - mainContainer:drawOnScreen() - uptime = computer.uptime() - end -end - -local function masterState(state) - for i = 1, #contorollersContainer.children do - local object = contorollersContainer.children[i] - if not object.disabled then - setButtonState(object.button, state) - end - end - - mainContainer:drawOnScreen() - - for i = 1, #redstones do - redstones[i].setOutput(1, state and 15 or 0) - end - - update() - mainContainer:drawOnScreen() -end - -masterDisable.onTouch = function() - masterState(false) -end - -masterEnable.onTouch = function() - masterState(true) -end - --------------------------------------------------------------------------------- - -update() -mainContainer:drawOnScreen(true) -mainContainer:startEventHandling(0) diff --git a/Applications/Settings/Icon.pic b/Applications/Settings.app/Icon.pic similarity index 100% rename from Applications/Settings/Icon.pic rename to Applications/Settings.app/Icon.pic diff --git a/Applications/Settings/Localizations/English.lang b/Applications/Settings.app/Localizations/English.lang similarity index 84% rename from Applications/Settings/Localizations/English.lang rename to Applications/Settings.app/Localizations/English.lang index d56022c2..c7e4f2cb 100644 --- a/Applications/Settings/Localizations/English.lang +++ b/Applications/Settings.app/Localizations/English.lang @@ -15,7 +15,7 @@ wallpaperWallpaper = "Wallpaper", wallpaperScreensaver = "Screensaver", wallpaperScreensaverPath = "Screensaver script path", - wallpaperScreensaverEnabled = "Wallpaper enabled", + wallpaperScreensaverEnabled = "Screensaver enabled", wallpaperScreensaverDelay = "Delay", wallpaperPath = "Wallpaper image path", wallpaperEnabled = "Wallpaper enabled", @@ -89,8 +89,16 @@ systemInfo = "This option saves memory by unloading unused libraries using the \"Weak Link\" method. Disable it if you encounter any rancors or glitches in the behavior of complex interdependent systems", users = "Users", - usersAdd = "Add user", - usersList = "List of users", - usersTypeNameHere = "Type nickname here", - usersInfo = "You can create private access to your computer. To register, the specified user must be online", + usersList = "User management", + usersEditIcon = "Edit icon", + usersChangeName = "Change name", + usersChangeNamePlaceholder = "Type new username", + usersRemovePassword = "Remove password", + usersAddPassword = "Add password", + usersAddPasswordPlaceholder1 = "Type password", + usersAddPasswordPlaceholder2 = "Submit given password", + usersPasswordsArentEqual = "Passwords aren't equal", + usersClaim = "Claim computer block", + usersClaimInfo = "Additionally, you can create private access to your computer block for player with given name. To do it, player must be online", + usersClaimPlaceholder = "Type online player name", } \ No newline at end of file diff --git a/Applications/Settings/Localizations/French.lang b/Applications/Settings.app/Localizations/French.lang similarity index 84% rename from Applications/Settings/Localizations/French.lang rename to Applications/Settings.app/Localizations/French.lang index 938cfa38..249fa014 100644 --- a/Applications/Settings/Localizations/French.lang +++ b/Applications/Settings.app/Localizations/French.lang @@ -88,9 +88,17 @@ systemUnload = "Télécharger la bibliothèque", systemInfo = "Cette option économise de la mémoire en déchargeant les bibliothèques inutilisées en utilisant la méthode \"Weak Link\". Désactivez-le si vous rencontrez des bizarreries ou des problèmes dans le comportement de systèmes interdépendants complexes", - users = "Utilisateurs", - usersAdd = "Ajouter un utilisateur", - usersList = "La liste des utilisateurs", - usersTypeNameHere = "Entrez un surnom", - usersInfo = "Vous pouvez créer un accès privé à votre ordinateur. Pour l'enregistrement, l'utilisateur spécifié doit être en ligne", + users = "Utilisateur", + usersList = "Gestion des utilisateurs", + usersEditIcon = "Icône de modification", + usersChangeName = "Le changement de nom", + usersChangeNamePlaceholder = "Tapez nouveau nom d'utilisateur", + usersRemovePassword = "Supprimer le mot de passe", + usersAddPassword = "Ajouter un mot de passe", + usersAddPasswordPlaceholder1 = "Type de mot de passe", + usersAddPasswordPlaceholder2 = "Soumettre un mot de passe donné", + usersPasswordsArentEqual = "Les mots de passe ne sont pas égaux", + usersClaim = "Bloc de demande d'indemnisation", + usersClaimInfo = "En outre, vous pouvez créer un accès privé à votre bloc d'ordinateur pour le Joueur avec le prénom. Pour le faire, le joueur doit être en ligne", + usersClaimPlaceholder = "Type Nom du lecteur en ligne", } \ No newline at end of file diff --git a/Applications/Settings/Localizations/Russian.lang b/Applications/Settings.app/Localizations/Russian.lang similarity index 85% rename from Applications/Settings/Localizations/Russian.lang rename to Applications/Settings.app/Localizations/Russian.lang index 520773c0..b9c857c5 100755 --- a/Applications/Settings/Localizations/Russian.lang +++ b/Applications/Settings.app/Localizations/Russian.lang @@ -89,8 +89,16 @@ systemInfo = "Данная опция позволяет экономить память, выгружая неиспользуемые библиотеки методом \"Weak Link\". Отключите ее, если столкнетесь с какими-либо странностями или глюками в поведении сложных взаимозависимых систем", users = "Пользователи", - usersAdd = "Добавить пользователя", - usersList = "Список пользователей", - usersTypeNameHere = "Введите никнейм", - usersInfo = "Вы можете создать приватный доступ к компьютеру. Для регистрации указанный пользователь должен быть онлайн", + usersList = "Управление пользователями", + usersEditIcon = "Редактировать иконку", + usersChangeName = "Изменить имя", + usersChangeNamePlaceholder = "Введите новое имя", + usersRemovePassword = "Удалить пароль", + usersAddPassword = "Добавить пароль", + usersAddPasswordPlaceholder1 = "Введите пароль", + usersAddPasswordPlaceholder2 = "Подтвердите пароль", + usersPasswordsArentEqual = "Пароли различаются", + usersClaim = "Приват блока компьютера", + usersClaimInfo = "Дополнительно вы можете создать приватный доступ к компьютерному блоку для игрока с указанными именем. Для этого игрок должен быть онлайн", + usersClaimPlaceholder = "Введите имя игрока", } \ No newline at end of file diff --git a/Applications/Settings/Localizations/Ukrainian.lang b/Applications/Settings.app/Localizations/Ukrainian.lang similarity index 85% rename from Applications/Settings/Localizations/Ukrainian.lang rename to Applications/Settings.app/Localizations/Ukrainian.lang index 38997cfd..70a23acf 100755 --- a/Applications/Settings/Localizations/Ukrainian.lang +++ b/Applications/Settings.app/Localizations/Ukrainian.lang @@ -89,8 +89,16 @@ systemInfo = "Дана опція дозволяє економити пам'ять, вивантажуючи невикористовувані бібліотеки методом \"Weak Link \". Вимкніть її, якщо зіткнетеся з якими-небудь дивацтвами або глюками в поведінці складних взаємозалежних систем", users = "Користувач", - usersAdd = "Додати користувача", - usersList = "Список користувачів", - usersTypeNameHere = "Введіть нікнейм", - usersInfo = "Ви можете створити приватний доступ до комп'ютера. Для реєстрації вказаний користувач повинен бути онлайн", + usersList = "Управління користувачами", + usersEditIcon = "Змінити значок", + usersChangeName = "Змінити ім'я", + usersChangeNamePlaceholder = "Введіть нове ім'я користувача", + usersRemovePassword = "Видалити пароль", + usersAddPassword = "Додати пароль", + usersAddPasswordPlaceholder1 = "Введіть пароль", + usersAddPasswordPlaceholder2 = "Надіслати пароль", + usersPasswordsArentEqual = "Паролі не рівні", + usersClaim = "Блок комп'ютера затвердження", + usersClaimInfo = "Крім того, ви можете створити приватний доступ до блоку вашого комп'ютера для гравця із зазначеним ім'ям. Для цього гравець повинен бути онлайн", + usersClaimPlaceholder = "Введіть ім'я гравця", } \ No newline at end of file diff --git a/Applications/Settings/Main.lua b/Applications/Settings.app/Main.lua similarity index 72% rename from Applications/Settings/Main.lua rename to Applications/Settings.app/Main.lua index e8295927..89cd57ab 100644 --- a/Applications/Settings/Main.lua +++ b/Applications/Settings.app/Main.lua @@ -1,25 +1,22 @@ -local computer = require("computer") -local component = require("component") local GUI = require("GUI") -local buffer = require("doubleBuffering") -local filesystem = require("filesystem") -local color = require("color") -local image = require("image") -local unicode = require("unicode") -local MineOSInterface = require("MineOSInterface") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") +local screen = require("Screen") +local filesystem = require("Filesystem") +local color = require("Color") +local image = require("Image") +local paths = require("Paths") +local system = require("System") -------------------------------------------------------------------------------- -local modulesPath = MineOSCore.getCurrentScriptDirectory() .. "Modules/" -local localization = MineOSCore.getCurrentScriptLocalization() +local currentScriptDirectory = filesystem.path(system.getCurrentScript()) +local modulesPath = currentScriptDirectory .. "Modules/" +local localization = system.getLocalization(currentScriptDirectory .. "Localizations/") local scrollSpeed = 2 -------------------------------------------------------------------------------- -local application, window = MineOSInterface.addWindow(GUI.filledWindow(1, 1, 100, 29, 0xF0F0F0)) +local workspace, window = system.addWindow(GUI.filledWindow(1, 1, 100, 29, 0xF0F0F0)) local leftPanel = window:addChild(GUI.panel(1, 1, 1, 1, 0x2D2D2D)) window.actionButtons.localY = 2 @@ -34,20 +31,20 @@ window.contentLayout = window:addChild(GUI.layout(1, 1, 1, 1, 1, 1)) local function moduleDraw(object) local textColor = object.pressed and 0x3C3C3C or 0xE1E1E1 if object.pressed then - buffer.drawRectangle(object.x, object.y, object.width, object.height, 0xF0F0F0, textColor, " ") - buffer.drawText(object.x, object.y - 1, 0xF0F0F0, string.rep("▄", object.width)) - buffer.drawText(object.x, object.y + object.height, 0xF0F0F0, string.rep("▀", object.width)) + screen.drawRectangle(object.x, object.y, object.width, object.height, 0xF0F0F0, textColor, " ") + screen.drawText(object.x, object.y - 1, 0xF0F0F0, string.rep("▄", object.width)) + screen.drawText(object.x, object.y + object.height, 0xF0F0F0, string.rep("▀", object.width)) end - buffer.drawImage(object.x + 2, object.y, object.icon) - buffer.drawText(object.x + 12, object.y + 1, textColor, object.module.name) + screen.drawImage(object.x + 2, object.y, object.icon) + screen.drawText(object.x + 12, object.y + 1, textColor, object.module.name) end local function runModule(object) window.contentLayout:removeChildren() window.contentLayout:setMargin(1, 1, 0, object.module.margin) - window.contentLayout.eventHandler = function(application, _, e1, e2, e3, e4, e5) + window.contentLayout.eventHandler = function(workspace, _, e1, e2, e3, e4, e5) if e1 == "scroll" then local cell = window.contentLayout.cells[1][1] local to = -math.floor(cell.childrenHeight / 2) @@ -59,12 +56,12 @@ local function runModule(object) cell.verticalMargin = to end - application:draw() + workspace:draw() end end object.module.onTouch() - application:draw() + workspace:draw() end local function selectModule(object) @@ -77,13 +74,13 @@ local function selectModule(object) runModule(object) end -local function moduleEventHandler(application, object, e1) +local function moduleEventHandler(workspace, object, e1) if e1 == "touch" then selectModule(object) end end -modulesLayout.eventHandler = function(application, object, e1, e2, e3, e4, e5) +modulesLayout.eventHandler = function(workspace, object, e1, e2, e3, e4, e5) if e1 == "scroll" then local cell = modulesLayout.cells[1][1] local to = -(#modulesLayout.children - 1) * 4 + 1 @@ -95,7 +92,7 @@ modulesLayout.eventHandler = function(application, object, e1, e2, e3, e4, e5) cell.verticalMargin = to end - application:draw() + workspace:draw() end end @@ -117,16 +114,13 @@ end -------------------------------------------------------------------------------- -local modules = {} -for file in filesystem.list(modulesPath) do - table.insert(modules, file) -end +local modules = filesystem.list(modulesPath) table.sort(modules, function(a, b) return a < b end) for i = 1, #modules do local result, reason = loadfile(modulesPath .. modules[i] .. "Main.lua") if result then - local success, result = pcall(result, application, window, localization) + local success, result = pcall(result, workspace, window, localization) if success then local object = modulesLayout:addChild(GUI.object(1, 1, 1, 3)) diff --git a/Applications/Settings/Modules/0_Screen/Icon.pic b/Applications/Settings.app/Modules/0_Screen/Icon.pic similarity index 100% rename from Applications/Settings/Modules/0_Screen/Icon.pic rename to Applications/Settings.app/Modules/0_Screen/Icon.pic diff --git a/Applications/Settings/Modules/0_Screen/Main.lua b/Applications/Settings.app/Modules/0_Screen/Main.lua similarity index 67% rename from Applications/Settings/Modules/0_Screen/Main.lua rename to Applications/Settings.app/Modules/0_Screen/Main.lua index 41821d59..0723a25e 100644 --- a/Applications/Settings/Modules/0_Screen/Main.lua +++ b/Applications/Settings.app/Modules/0_Screen/Main.lua @@ -1,15 +1,12 @@ local GUI = require("GUI") -local component = require("component") -local buffer = require("doubleBuffering") -local MineOSInterface = require("MineOSInterface") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") -local scale = require("scale") +local screen = require("Screen") +local paths = require("Paths") +local system = require("System") local module = {} -local application, window, localization = table.unpack({...}) +local workspace, window, localization = table.unpack({...}) -------------------------------------------------------------------------------- @@ -22,14 +19,16 @@ module.onTouch = function() local monitorComboBox = window.contentLayout:addChild(GUI.comboBox(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xD2D2D2, 0xA5A5A5)) for address in component.list("screen") do monitorComboBox:addItem(address).onTouch = function() - buffer.clear(0x0) - buffer.drawChanges() + screen.clear(0x0) + screen.update() - buffer.bindScreen(address, false) - MineOSInterface.changeResolution() - MineOSInterface.changeWallpaper() - MineOSInterface.updateFileListAndDraw() - MineOSCore.saveProperties() + screen.bind(address, false) + + system.updateResolution() + system.updateWallpaper() + workspace:draw() + + system.saveProperties() end end @@ -38,17 +37,19 @@ module.onTouch = function() local resolutionComboBox = window.contentLayout:addChild(GUI.comboBox(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xD2D2D2, 0xA5A5A5)) local function setResolution(width, height) - MineOSCore.properties.resolution = {width, height} - MineOSInterface.changeResolution() - MineOSInterface.changeWallpaper() - MineOSInterface.updateFileListAndDraw() + system.properties.interfaceScreenWidth = width + system.properties.interfaceScreenHeight = height + + system.updateResolution() + system.updateWallpaper() + workspace:draw() - MineOSCore.saveProperties() + system.saveProperties() end local step = 1 / 6 for i = 1, step, -step do - local width, height = scale.getResolution(i) + local width, height = screen.getScaledResolution(i) resolutionComboBox:addItem(width .. "x" .. height).onTouch = function() setResolution(width, height) end @@ -61,17 +62,17 @@ module.onTouch = function() layout:addChild(GUI.text(1, 1, 0x2D2D2D, "x")) local heightInput = layout:addChild(GUI.input(1, 1, 17, 3, 0xE1E1E1, 0x696969, 0xA5A5A5, 0xE1E1E1, 0x2D2D2D, "", localization.screenHeight)) - local maxWidth, maxHeight = buffer.getGPUProxy().maxResolution() + local maxWidth, maxHeight = screen.getGPUProxy().maxResolution() local limit = maxWidth * maxHeight local cykaTextBox = window.contentLayout:addChild(GUI.textBox(1, 1, 36, 1, nil, 0x880000, {string.format(localization.screenInvalidResolution, limit)}, 1, 0, 0, true, true)) - local switch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.screenAutoScale .. ":", MineOSCore.properties.screenAutoScale)).switch + local switch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.screenAutoScale .. ":", system.properties.interfaceScreenAutoScale)).switch window.contentLayout:addChild(GUI.textBox(1, 1, 36, 1, nil, 0xA5A5A5, {localization.screenScaleInfo}, 1, 0, 0, true, true)) local function updateSwitch() - widthInput.text = tostring(MineOSCore.properties.resolution and MineOSCore.properties.resolution[1] or buffer.getWidth()) - heightInput.text = tostring(MineOSCore.properties.resolution and MineOSCore.properties.resolution[2] or buffer.getHeight()) + widthInput.text = tostring(system.properties.interfaceScreenWidth and system.properties.interfaceScreenWidth or screen.getWidth()) + heightInput.text = tostring(system.properties.interfaceScreenHeight and system.properties.interfaceScreenHeight or screen.getHeight()) resolutionComboBox.hidden = not switch.state layout.hidden = switch.state end @@ -85,10 +86,10 @@ module.onTouch = function() switch.onStateChanged = function() updateSwitch() updateCykaTextBox() - application:draw() + workspace:draw() - MineOSCore.properties.screenAutoScale = switch.state - MineOSCore.saveProperties() + system.properties.interfaceScreenAutoScale = switch.state + system.saveProperties() end widthInput.onInputFinished = function() @@ -96,7 +97,7 @@ module.onTouch = function() if cykaTextBox.hidden then setResolution(width, height) else - application:draw() + workspace:draw() end end heightInput.onInputFinished = widthInput.onInputFinished diff --git a/Applications/Settings/Modules/1_Wallpaper/Icon.pic b/Applications/Settings.app/Modules/1_Wallpaper/Icon.pic similarity index 100% rename from Applications/Settings/Modules/1_Wallpaper/Icon.pic rename to Applications/Settings.app/Modules/1_Wallpaper/Icon.pic diff --git a/Applications/Settings/Modules/1_Wallpaper/Main.lua b/Applications/Settings.app/Modules/1_Wallpaper/Main.lua similarity index 52% rename from Applications/Settings/Modules/1_Wallpaper/Main.lua rename to Applications/Settings.app/Modules/1_Wallpaper/Main.lua index 2d7e5f5a..748a1296 100644 --- a/Applications/Settings/Modules/1_Wallpaper/Main.lua +++ b/Applications/Settings.app/Modules/1_Wallpaper/Main.lua @@ -1,12 +1,10 @@ local GUI = require("GUI") -local MineOSInterface = require("MineOSInterface") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") +local system = require("System") local module = {} -local application, window, localization = table.unpack({...}) +local workspace, window, localization = table.unpack({...}) -------------------------------------------------------------------------------- @@ -15,68 +13,68 @@ module.margin = 5 module.onTouch = function() window.contentLayout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.wallpaperWallpaper)) - local wallpaperChooser = window.contentLayout:addChild(GUI.filesystemChooser(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xD2D2D2, 0xA5A5A5, MineOSCore.properties.wallpaper, localization.open, localization.cancel, localization.wallpaperPath, "/")) + local wallpaperChooser = window.contentLayout:addChild(GUI.filesystemChooser(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xD2D2D2, 0xA5A5A5, system.properties.interfaceWallpaperPath, localization.open, localization.cancel, localization.wallpaperPath, "/")) wallpaperChooser:setMode(GUI.IO_MODE_OPEN, GUI.IO_MODE_FILE) wallpaperChooser:addExtensionFilter(".pic") wallpaperChooser.onSubmit = function(path) - MineOSCore.properties.wallpaper = path - MineOSInterface.changeWallpaper() - MineOSInterface.application:draw() + system.properties.interfaceWallpaperPath = path + system.updateWallpaper() + workspace:draw() - MineOSCore.saveProperties() + system.saveProperties() end local comboBox = window.contentLayout:addChild(GUI.comboBox(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xD2D2D2, 0xA5A5A5)) - comboBox.selectedItem = MineOSCore.properties.wallpaperMode or 1 + comboBox.selectedItem = system.properties.interfaceWallpaperMode or 1 comboBox:addItem(localization.wallpaperStretch) comboBox:addItem(localization.wallpaperCenter) - local wallpaperSwitch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.wallpaperEnabled .. ":", MineOSCore.properties.wallpaperEnabled)).switch + local wallpaperSwitch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.wallpaperEnabled .. ":", system.properties.interfaceWallpaperEnabled)).switch wallpaperSwitch.onStateChanged = function() - MineOSCore.properties.wallpaperEnabled = wallpaperSwitch.state - MineOSInterface.changeWallpaper() - MineOSInterface.application:draw() + system.properties.interfaceWallpaperEnabled = wallpaperSwitch.state + system.updateWallpaper() + workspace:draw() - MineOSCore.saveProperties() + system.saveProperties() end window.contentLayout:addChild(GUI.textBox(1, 1, 36, 1, nil, 0xA5A5A5, {localization.wallpaperInfo}, 1, 0, 0, true, true)) - local wallpaperSlider = window.contentLayout:addChild(GUI.slider(1, 1, 36, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, 0, 100, MineOSCore.properties.wallpaperBrightness * 100, false, localization.wallpaperBrightness .. ": ", "%")) + local wallpaperSlider = window.contentLayout:addChild(GUI.slider(1, 1, 36, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, 0, 100, system.properties.interfaceWallpaperBrightness * 100, false, localization.wallpaperBrightness .. ": ", "%")) wallpaperSlider.height = 2 wallpaperSlider.roundValues = true wallpaperSlider.onValueChanged = function() - MineOSCore.properties.wallpaperBrightness = wallpaperSlider.value / 100 - MineOSInterface.changeWallpaper() - MineOSInterface.application:draw() + system.properties.interfaceWallpaperBrightness = wallpaperSlider.value / 100 + system.updateWallpaper() + workspace:draw() - MineOSCore.saveProperties() + system.saveProperties() end comboBox.onItemSelected = function() - MineOSCore.properties.wallpaperMode = comboBox.selectedItem - MineOSInterface.changeWallpaper() - MineOSInterface.application:draw() + system.properties.interfaceWallpaperMode = comboBox.selectedItem + system.updateWallpaper() + workspace:draw() - MineOSCore.saveProperties() + system.saveProperties() end window.contentLayout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.wallpaperScreensaver)) - local screensaverChooser = window.contentLayout:addChild(GUI.filesystemChooser(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xD2D2D2, 0xA5A5A5, MineOSCore.properties.screensaver, localization.open, localization.cancel, localization.wallpaperScreensaverPath, "/")) + local screensaverChooser = window.contentLayout:addChild(GUI.filesystemChooser(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xD2D2D2, 0xA5A5A5, system.properties.interfaceScreensaverPath, localization.open, localization.cancel, localization.wallpaperScreensaverPath, "/")) screensaverChooser:setMode(GUI.IO_MODE_OPEN, GUI.IO_MODE_FILE) screensaverChooser:addExtensionFilter(".lua") - local screensaverSwitch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.wallpaperScreensaverEnabled .. ":", MineOSCore.properties.screensaverEnabled)).switch + local screensaverSwitch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.wallpaperScreensaverEnabled .. ":", system.properties.interfaceScreensaverEnabled)).switch - local screensaverSlider = window.contentLayout:addChild(GUI.slider(1, 1, 36, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, 1, 100, MineOSCore.properties.screensaverDelay, false, localization.wallpaperScreensaverDelay .. ": ", " s")) + local screensaverSlider = window.contentLayout:addChild(GUI.slider(1, 1, 36, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, 1, 100, system.properties.interfaceScreensaverDelay, false, localization.wallpaperScreensaverDelay .. ": ", " s")) local function save() - MineOSCore.properties.screensaverEnabled = screensaverSwitch.state - MineOSCore.properties.screensaver = screensaverChooser.path - MineOSCore.properties.screensaverDelay = screensaverSlider.value + system.properties.interfaceScreensaverEnabled = screensaverSwitch.state + system.properties.interfaceScreensaverPath = screensaverChooser.path + system.properties.interfaceScreensaverDelay = screensaverSlider.value - MineOSCore.saveProperties() + system.saveProperties() end screensaverChooser.onSubmit, screensaverSwitch.onStateChanged, screensaverSlider.onValueChanged = save, save, save diff --git a/Applications/Settings/Modules/2_Icons/Icon.pic b/Applications/Settings.app/Modules/2_Icons/Icon.pic similarity index 100% rename from Applications/Settings/Modules/2_Icons/Icon.pic rename to Applications/Settings.app/Modules/2_Icons/Icon.pic diff --git a/Applications/Settings/Modules/2_Icons/Main.lua b/Applications/Settings.app/Modules/2_Icons/Main.lua similarity index 51% rename from Applications/Settings/Modules/2_Icons/Main.lua rename to Applications/Settings.app/Modules/2_Icons/Main.lua index 35f01370..ff13f782 100644 --- a/Applications/Settings/Modules/2_Icons/Main.lua +++ b/Applications/Settings.app/Modules/2_Icons/Main.lua @@ -1,13 +1,11 @@ local GUI = require("GUI") -local computer = require("computer") -local MineOSInterface = require("MineOSInterface") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") +local paths = require("Paths") +local system = require("System") local module = {} -local application, window, localization = table.unpack({...}) +local workspace, window, localization = table.unpack({...}) -------------------------------------------------------------------------------- @@ -16,72 +14,75 @@ module.margin = 12 module.onTouch = function() window.contentLayout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.appearanceFiles)) - local showExtensionSwitch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.appearanceExtensions .. ":", MineOSCore.properties.showExtension)).switch - local showHiddenFilesSwitch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.appearanceHidden .. ":", MineOSCore.properties.showHiddenFiles)).switch - local showApplicationIconsSwitch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.appearanceApplications .. ":", MineOSCore.properties.showApplicationIcons)).switch - local transparencySwitch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.appearanceTransparencyEnabled .. ":", MineOSCore.properties.transparencyEnabled)).switch + local showExtensionSwitch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.appearanceExtensions .. ":", system.properties.filesShowExtension)).switch + local showHiddenFilesSwitch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.appearanceHidden .. ":", system.properties.filesShowHidden)).switch + local showApplicationIconsSwitch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.appearanceApplications .. ":", system.properties.filesShowApplicationIcon)).switch + local transparencySwitch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.appearanceTransparencyEnabled .. ":", system.properties.interfaceTransparencyEnabled)).switch window.contentLayout:addChild(GUI.textBox(1, 1, 36, 1, nil, 0xA5A5A5, {localization.appearanceTransparencyInfo}, 1, 0, 0, true, true)) window.contentLayout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.appearanceColorScheme)) - local backgroundColorSelector = window.contentLayout:addChild(GUI.colorSelector(1, 1, 36, 3, MineOSCore.properties.backgroundColor, localization.appearanceDesktopBackground)) - local menuColorSelector = window.contentLayout:addChild(GUI.colorSelector(1, 1, 36, 3, MineOSCore.properties.menuColor, localization.appearanceMenu)) - local dockColorSelector = window.contentLayout:addChild(GUI.colorSelector(1, 1, 36, 3, MineOSCore.properties.dockColor, localization.appearanceDock)) + local backgroundColorSelector = window.contentLayout:addChild(GUI.colorSelector(1, 1, 36, 3, system.properties.interfaceColorDesktopBackground, localization.appearanceDesktopBackground)) + local menuColorSelector = window.contentLayout:addChild(GUI.colorSelector(1, 1, 36, 3, system.properties.interfaceColorMenu, localization.appearanceMenu)) + local dockColorSelector = window.contentLayout:addChild(GUI.colorSelector(1, 1, 36, 3, system.properties.interfaceColorDock, localization.appearanceDock)) backgroundColorSelector.onColorSelected = function() - MineOSCore.properties.backgroundColor = backgroundColorSelector.color - MineOSCore.properties.menuColor = menuColorSelector.color - MineOSCore.properties.dockColor = dockColorSelector.color - MineOSInterface.application.menu.colors.default.background = MineOSCore.properties.menuColor + system.properties.interfaceColorDesktopBackground = backgroundColorSelector.color + system.properties.interfaceColorMenu = menuColorSelector.color + system.properties.interfaceColorDock = dockColorSelector.color + system.properties.interfaceTransparencyEnabled = transparencySwitch.state - MineOSInterface.application:draw() + system.updateColorScheme() + workspace:draw() + system.saveProperties() end menuColorSelector.onColorSelected = backgroundColorSelector.onColorSelected dockColorSelector.onColorSelected = backgroundColorSelector.onColorSelected + transparencySwitch.onStateChanged = backgroundColorSelector.onColorSelected window.contentLayout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.appearanceSize)) - local iconWidthSlider = window.contentLayout:addChild(GUI.slider(1, 1, 36, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, 8, 16, MineOSCore.properties.iconWidth, false, localization.appearanceHorizontal .. ": ", "")) - local iconHeightSlider = window.contentLayout:addChild(GUI.slider(1, 1, 36, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, 6, 16, MineOSCore.properties.iconHeight, false, localization.appearanceVertical .. ": ", "")) + local iconWidthSlider = window.contentLayout:addChild(GUI.slider(1, 1, 36, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, 8, 16, system.properties.iconWidth, false, localization.appearanceHorizontal .. ": ", "")) + local iconHeightSlider = window.contentLayout:addChild(GUI.slider(1, 1, 36, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, 6, 16, system.properties.iconHeight, false, localization.appearanceVertical .. ": ", "")) iconHeightSlider.height = 2 window.contentLayout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.appearanceSpace)) - local iconHorizontalSpaceBetweenSlider = window.contentLayout:addChild(GUI.slider(1, 1, 36, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, 0, 5, MineOSCore.properties.iconHorizontalSpaceBetween, false, localization.appearanceHorizontal .. ": ", "")) - local iconVerticalSpaceBetweenSlider = window.contentLayout:addChild(GUI.slider(1, 1, 36, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, 0, 5, MineOSCore.properties.iconVerticalSpaceBetween, false, localization.appearanceVertical .. ": ", "")) + local iconHorizontalSpaceBetweenSlider = window.contentLayout:addChild(GUI.slider(1, 1, 36, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, 0, 5, system.properties.iconHorizontalSpace, false, localization.appearanceHorizontal .. ": ", "")) + local iconVerticalSpaceBetweenSlider = window.contentLayout:addChild(GUI.slider(1, 1, 36, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, 0, 5, system.properties.iconVerticalSpace, false, localization.appearanceVertical .. ": ", "")) iconVerticalSpaceBetweenSlider.height = 2 iconHorizontalSpaceBetweenSlider.roundValues, iconVerticalSpaceBetweenSlider.roundValues = true, true iconWidthSlider.roundValues, iconHeightSlider.roundValues = true, true + local function setIconProperties(width, height, horizontalSpace, verticalSpace) + system.properties.iconWidth, system.properties.iconHeight, system.properties.iconHorizontalSpace, system.properties.iconVerticalSpace = width, height, horizontalSpace, verticalSpace + system.saveProperties() + + system.calculateIconProperties() + system.updateIconProperties() + end + iconWidthSlider.onValueChanged = function() - MineOSInterface.setIconProperties(math.floor(iconWidthSlider.value), math.floor(iconHeightSlider.value), MineOSCore.properties.iconHorizontalSpaceBetween, MineOSCore.properties.iconVerticalSpaceBetween) + setIconProperties(math.floor(iconWidthSlider.value), math.floor(iconHeightSlider.value), system.properties.iconHorizontalSpace, system.properties.iconVerticalSpace) end iconHeightSlider.onValueChanged = iconWidthSlider.onValueChanged iconHorizontalSpaceBetweenSlider.onValueChanged = function() - MineOSInterface.setIconProperties(MineOSCore.properties.iconWidth, MineOSCore.properties.iconHeight, math.floor(iconHorizontalSpaceBetweenSlider.value), math.floor(iconVerticalSpaceBetweenSlider.value)) + setIconProperties(system.properties.iconWidth, system.properties.iconHeight, math.floor(iconHorizontalSpaceBetweenSlider.value), math.floor(iconVerticalSpaceBetweenSlider.value)) end iconVerticalSpaceBetweenSlider.onValueChanged = iconHorizontalSpaceBetweenSlider.onValueChanged showExtensionSwitch.onStateChanged = function() - MineOSCore.properties.showExtension = showExtensionSwitch.state - MineOSCore.properties.showHiddenFiles = showHiddenFilesSwitch.state - MineOSCore.properties.showApplicationIcons = showApplicationIconsSwitch.state - MineOSCore.saveProperties() + system.properties.filesShowExtension = showExtensionSwitch.state + system.properties.filesShowHidden = showHiddenFilesSwitch.state + system.properties.filesShowApplicationIcon = showApplicationIconsSwitch.state + system.saveProperties() - computer.pushSignal("MineOSCore", "updateFileList") + computer.pushSignal("system", "updateFileList") end showHiddenFilesSwitch.onStateChanged, showApplicationIconsSwitch.onStateChanged = showExtensionSwitch.onStateChanged, showExtensionSwitch.onStateChanged - - transparencySwitch.onStateChanged = function() - MineOSCore.properties.transparencyEnabled = transparencySwitch.state - - MineOSInterface.applyTransparency() - MineOSInterface.application:draw() - MineOSCore.saveProperties() - end end diff --git a/Applications/Settings/Modules/3_Tasks/Icon.pic b/Applications/Settings.app/Modules/3_Tasks/Icon.pic similarity index 100% rename from Applications/Settings/Modules/3_Tasks/Icon.pic rename to Applications/Settings.app/Modules/3_Tasks/Icon.pic diff --git a/Applications/Settings/Modules/3_Tasks/Main.lua b/Applications/Settings.app/Modules/3_Tasks/Main.lua similarity index 66% rename from Applications/Settings/Modules/3_Tasks/Main.lua rename to Applications/Settings.app/Modules/3_Tasks/Main.lua index 528729cf..6be24c48 100644 --- a/Applications/Settings/Modules/3_Tasks/Main.lua +++ b/Applications/Settings.app/Modules/3_Tasks/Main.lua @@ -1,12 +1,11 @@ local GUI = require("GUI") -local MineOSInterface = require("MineOSInterface") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") +local paths = require("Paths") +local system = require("System") local module = {} -local application, window, localization = table.unpack({...}) +local workspace, window, localization = table.unpack({...}) -------------------------------------------------------------------------------- @@ -27,21 +26,21 @@ module.onTouch = function() local switchAndLabel = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.tasksEnabled .. ":", true)) local function update() - switchAndLabel.hidden = #MineOSCore.properties.tasks == 0 + switchAndLabel.hidden = #system.properties.tasks == 0 modeComboBox.hidden = switchAndLabel.hidden container.hidden = switchAndLabel.hidden if not switchAndLabel.hidden then - modeComboBox.selectedItem = MineOSCore.properties.tasks[tasksComboBox.selectedItem].mode - switchAndLabel.switch:setState(MineOSCore.properties.tasks[tasksComboBox.selectedItem].enabled) + modeComboBox.selectedItem = system.properties.tasks[tasksComboBox.selectedItem].mode + switchAndLabel.switch:setState(system.properties.tasks[tasksComboBox.selectedItem].enabled) end end local function fill() tasksComboBox:clear() - for i = 1, #MineOSCore.properties.tasks do - tasksComboBox:addItem(MineOSCore.properties.tasks[i].path) + for i = 1, #system.properties.tasks do + tasksComboBox:addItem(system.properties.tasks[i].path) end tasksComboBox.selectedItem = tasksComboBox:count() @@ -51,7 +50,7 @@ module.onTouch = function() tasksComboBox.onItemSelected = update filesystemChooser.onSubmit = function(path) - table.insert(MineOSCore.properties.tasks, { + table.insert(system.properties.tasks, { path = filesystemChooser.path, enabled = switchAndLabel.switch.state, mode = modeComboBox.selectedItem, @@ -60,27 +59,27 @@ module.onTouch = function() filesystemChooser.path = nil fill() - MineOSCore.saveProperties() + system.saveProperties() end removeButton.onTouch = function() - table.remove(MineOSCore.properties.tasks, tasksComboBox.selectedItem) + table.remove(system.properties.tasks, tasksComboBox.selectedItem) fill() - MineOSCore.saveProperties() + system.saveProperties() end modeComboBox.onItemSelected = function() - if #MineOSCore.properties.tasks > 0 then - MineOSCore.properties.tasks[tasksComboBox.selectedItem].mode = modeComboBox.selectedItem - MineOSCore.saveProperties() + if #system.properties.tasks > 0 then + system.properties.tasks[tasksComboBox.selectedItem].mode = modeComboBox.selectedItem + system.saveProperties() end end switchAndLabel.switch.onStateChanged = function() - if #MineOSCore.properties.tasks > 0 then - MineOSCore.properties.tasks[tasksComboBox.selectedItem].enabled = switchAndLabel.switch.state - MineOSCore.saveProperties() + if #system.properties.tasks > 0 then + system.properties.tasks[tasksComboBox.selectedItem].enabled = switchAndLabel.switch.state + system.saveProperties() end end diff --git a/Applications/Settings/Modules/00_Users/Icon.pic b/Applications/Settings.app/Modules/40_Users/Icon.pic similarity index 100% rename from Applications/Settings/Modules/00_Users/Icon.pic rename to Applications/Settings.app/Modules/40_Users/Icon.pic diff --git a/Applications/Settings.app/Modules/40_Users/Main.lua b/Applications/Settings.app/Modules/40_Users/Main.lua new file mode 100644 index 00000000..099f376b --- /dev/null +++ b/Applications/Settings.app/Modules/40_Users/Main.lua @@ -0,0 +1,227 @@ + +local GUI = require("GUI") +local text = require("Text") +local filesystem = require("Filesystem") +local paths = require("Paths") +local system = require("System") +local image = require("Image") +local SHA = require("SHA-256") + +local module = {} + +local workspace, window, localization = table.unpack({...}) + +-------------------------------------------------------------------------------- + +module.name = localization.users +module.margin = 7 +module.onTouch = function() + window.contentLayout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.usersList)) + + local function addButton(parent, x, width, ...) + local button = parent:addChild(GUI.button(x, 1, width, 3, 0xE1E1E1, 0x696969, 0x696969, 0xE1E1E1, ...)) + button.colors.disabled = { + background = 0xE1E1E1, + text = 0xB4B4B4 + } + + return button + end + + local function addInput(parent, ...) + return parent:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xA5A5A5, 0xE1E1E1, 0x2D2D2D, ...)) + end + + local function addComboBox(parent, width) + return parent:addChild(GUI.comboBox(1, 1, width, 3, 0xE1E1E1, 0x696969, 0xD2D2D2, 0xA5A5A5)) + end + + local usersContainer = window.contentLayout:addChild(GUI.container(1, 1, 36, 3)) + local removeUserButton = addButton(usersContainer, usersContainer.width - 4, 5, "─") + local addUserButton = addButton(usersContainer, removeUserButton.localX - 6, 5, "+") + local usersComboBox = addComboBox(usersContainer, addUserButton.localX - 2) + + local iconButton = addButton(window.contentLayout, 1, 36, localization.usersEditIcon) + + local renameButton = addButton(window.contentLayout, 1, 36, localization.usersChangeName) + local renameInput = addInput(window.contentLayout, "", localization.usersChangeNamePlaceholder) + + local passwordButton = addButton(window.contentLayout, 1, 36, "") + local passwordInput = addInput(window.contentLayout, "", localization.usersAddPasswordPlaceholder1, false, "•") + local submitPasswordInput = addInput(window.contentLayout, "", localization.usersAddPasswordPlaceholder2, false, "•") + local passwordText = window.contentLayout:addChild(GUI.text(1, 1, 0xCC4940, localization.usersPasswordsArentEqual)) + + local function updatePasswordText() + passwordButton.text = system.properties.securityPassword and localization.usersRemovePassword or localization.usersAddPassword + end + + local function updateRename(state) + renameButton.hidden = state + renameInput.hidden = not state + end + + local function getSelected() + return usersComboBox:getItem(usersComboBox.selectedItem).text + end + + local function updateRemoveAndPasswordButtons(state) + if system.getUser() == getSelected() then + removeUserButton.disabled = true + passwordButton.hidden = state + passwordInput.hidden = not state + submitPasswordInput.hidden = not state + else + removeUserButton.disabled = false + passwordButton.hidden = true + passwordInput.hidden = true + submitPasswordInput.hidden = true + passwordText.hidden = true + end + end + + local function updateUserList() + usersComboBox:clear() + + local list = filesystem.list(paths.system.users) + for i = 1, #list do + if filesystem.isDirectory(paths.system.users .. list[i]) then + local name = list[i]:sub(1, -2) + usersComboBox:addItem(name) + + if name == system.getUser() then + usersComboBox.selectedItem = usersComboBox:count() + end + end + end + + updateRemoveAndPasswordButtons() + end + + usersComboBox.onItemSelected = function() + updateRemoveAndPasswordButtons() + end + + updatePasswordText() + updateUserList() + + renameInput.hidden = true + passwordInput.hidden = true + submitPasswordInput.hidden = true + passwordText.hidden = true + + addUserButton.onTouch = function() + local name = "User #" .. math.random(0xFFFFFF) + system.createUser(name, system.properties.localizationLanguage, nil, true, true) + usersComboBox:addItem(name) + usersComboBox.selectedItem = usersComboBox:count() + end + + removeUserButton.onTouch = function() + filesystem.remove(paths.system.users .. getSelected() .. "/") + updateUserList() + end + + iconButton.onTouch = function() + system.execute(paths.system.applicationPictureEdit, paths.system.users .. getSelected() .. "/Icon.pic", "-o") + end + + renameButton.onTouch = function() + updateRename(true) + end + + passwordButton.onTouch = function() + if system.properties.securityPassword then + system.properties.securityPassword = nil + updatePasswordText() + + workspace:draw() + system.saveProperties() + else + updateRemoveAndPasswordButtons(true) + end + end + + renameInput.onInputFinished = function() + if #renameInput.text > 0 then + filesystem.rename(paths.system.users .. getSelected() .. "/", paths.system.users .. renameInput.text .. "/") + + if getSelected() == system.getUser() then + system.setUser(renameInput.text) + computer.pushSignal("system", "updateFileList") + end + + updateUserList() + renameInput.text = "" + updateRename(false) + + workspace:draw() + end + end + + passwordInput.onInputFinished = function() + if #passwordInput.text > 0 and #submitPasswordInput.text > 0 then + if passwordInput.text == submitPasswordInput.text then + system.properties.securityPassword = SHA.hash(passwordInput.text) + system.saveProperties() + + passwordInput.text = "" + submitPasswordInput.text = "" + updatePasswordText() + updateRemoveAndPasswordButtons() + + passwordText.hidden = true + else + passwordText.hidden = false + end + + workspace:draw() + end + end + submitPasswordInput.onInputFinished = passwordInput.onInputFinished + + window.contentLayout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.usersClaim)) + + window.contentLayout:addChild(GUI.textBox(1, 1, 36, 1, nil, 0xA5A5A5, {localization.usersClaimInfo}, 1, 0, 0, true, true)) + + local claimContainer = window.contentLayout:addChild(GUI.container(1, 1, 36, 3)) + local claimRemoveButton = addButton(claimContainer, claimContainer.width - 4, 5, "─") + local claimComboBox = addComboBox(claimContainer, claimRemoveButton.localX - 2) + + local claimInput = window.contentLayout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xA5A5A5, 0xE1E1E1, 0x2D2D2D, "", localization.usersClaimPlaceholder)) + + + local function update() + local users = {computer.users()} + -- local users = {"ECS", "Xylic", "Computrix", "Yan0t", "Кукарек", "Bird", "Pirnogion"} + + claimContainer.hidden = #users == 0 + claimComboBox:clear() + + for i = 1, #users do + claimComboBox:addItem(users[i]) + end + end + + claimRemoveButton.onTouch = function() + computer.removeUser(claimComboBox:getItem(claimComboBox.selectedItem).text) + + update() + workspace:draw() + end + + claimInput.onInputFinished = function() + if #claimInput.text > 0 then + computer.addUser(claimInput.text) + claimInput.text = "" + + update() + workspace:draw() + end + end + + update() +end + +-------------------------------------------------------------------------------- + +return module diff --git a/Applications/Settings/Modules/4_Disks/Icon.pic b/Applications/Settings.app/Modules/4_Disks/Icon.pic similarity index 100% rename from Applications/Settings/Modules/4_Disks/Icon.pic rename to Applications/Settings.app/Modules/4_Disks/Icon.pic diff --git a/Applications/Settings/Modules/4_Disks/Main.lua b/Applications/Settings.app/Modules/4_Disks/Main.lua similarity index 88% rename from Applications/Settings/Modules/4_Disks/Main.lua rename to Applications/Settings.app/Modules/4_Disks/Main.lua index 191cd26e..e4ce3981 100644 --- a/Applications/Settings/Modules/4_Disks/Main.lua +++ b/Applications/Settings.app/Modules/4_Disks/Main.lua @@ -1,22 +1,19 @@ -local computer = require("computer") -local component = require("component") local GUI = require("GUI") -local MineOSInterface = require("MineOSInterface") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") -local filesystem = require("filesystem") +local paths = require("Paths") +local system = require("System") +local filesystem = require("Filesystem") local module = {} -local application, window, localization = table.unpack({...}) +local workspace, window, localization = table.unpack({...}) -------------------------------------------------------------------------------- module.name = localization.disks module.margin = 6 module.onTouch = function() - local currentAddress = computer.getBootAddress() + local currentAddress = component.proxy(component.list("eeprom")()).getData() window.contentLayout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.disksControl)) @@ -79,7 +76,7 @@ module.onTouch = function() comboBox.onItemSelected = function() currentAddress = getProxy().address update() - application:draw() + workspace:draw() end input.onInputFinished = function() @@ -92,7 +89,7 @@ module.onTouch = function() GUI.alert(reason) end - application:draw() + workspace:draw() end button.onTouch = function() @@ -103,7 +100,7 @@ module.onTouch = function() end fill() - application:draw() + workspace:draw() end fill() diff --git a/Applications/Settings/Modules/5_Network/Icon.pic b/Applications/Settings.app/Modules/5_Network/Icon.pic similarity index 100% rename from Applications/Settings/Modules/5_Network/Icon.pic rename to Applications/Settings.app/Modules/5_Network/Icon.pic diff --git a/Applications/Settings/Modules/5_Network/Main.lua b/Applications/Settings.app/Modules/5_Network/Main.lua similarity index 60% rename from Applications/Settings/Modules/5_Network/Main.lua rename to Applications/Settings.app/Modules/5_Network/Main.lua index 63d7bdf0..e5502ecc 100644 --- a/Applications/Settings/Modules/5_Network/Main.lua +++ b/Applications/Settings.app/Modules/5_Network/Main.lua @@ -1,14 +1,13 @@ local GUI = require("GUI") -local MineOSInterface = require("MineOSInterface") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") -local MineOSNetwork = require("MineOSNetwork") -local filesystem = require("filesystem") +local paths = require("Paths") +local system = require("System") +local network = require("Network") +local filesystem = require("Filesystem") local module = {} -local application, window, localization = table.unpack({...}) +local workspace, window, localization = table.unpack({...}) -------------------------------------------------------------------------------- @@ -18,37 +17,37 @@ module.onTouch = function() local emptyObject = window.contentLayout:addChild(GUI.object(1, 1, 0, 0)) local insertModemText = window.contentLayout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.networkNoModem)) local ebloText = window.contentLayout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.networkThis)) - local networkNameInput = window.contentLayout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xA5A5A5, 0xE1E1E1, 0x2D2D2D, MineOSCore.properties.network.name or "", localization.networkName)) - local stateSwitchAndLabel = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.networkEnabled .. ":", MineOSCore.properties.network.enabled)) + local networkNameInput = window.contentLayout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xA5A5A5, 0xE1E1E1, 0x2D2D2D, system.properties.networkName or "", localization.networkName)) + local stateSwitchAndLabel = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.networkEnabled .. ":", system.properties.networkEnabled)) local remoteComputersText = window.contentLayout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.networkRemote)) local remoteComputersComboBox = window.contentLayout:addChild(GUI.comboBox(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xD2D2D2, 0xA5A5A5)) local allowReadAndWriteSwitchAndLabel = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.networkFileAccess .. ":", false)) - local signalStrengthSlider = window.contentLayout:addChild(GUI.slider(1, 1, 36, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, 0, 512, MineOSCore.properties.network.signalStrength, false, localization.networkRadius ..": ", "")) + local signalStrengthSlider = window.contentLayout:addChild(GUI.slider(1, 1, 36, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, 0, 512, system.properties.networkSignalStrength, false, localization.networkRadius ..": ", "")) signalStrengthSlider.roundValues = true local function check() - insertModemText.hidden = MineOSNetwork.modemProxy + insertModemText.hidden = network.modemProxy for i = 3, #window.contentLayout.children do - window.contentLayout.children[i].hidden = not MineOSNetwork.modemProxy + window.contentLayout.children[i].hidden = not network.modemProxy end - if MineOSNetwork.modemProxy then + if network.modemProxy then for i = 6, #window.contentLayout.children do window.contentLayout.children[i].hidden = not stateSwitchAndLabel.switch.state end if stateSwitchAndLabel.switch.state then - signalStrengthSlider.hidden = not MineOSNetwork.modemProxy.isWireless() + signalStrengthSlider.hidden = not network.modemProxy.isWireless() remoteComputersComboBox:clear() for proxy, path in filesystem.mounts() do - if proxy.MineOSNetworkModem then - local item = remoteComputersComboBox:addItem(MineOSNetwork.getModemProxyName(proxy)) + if proxy.networkModem then + local item = remoteComputersComboBox:addItem(network.getModemProxyName(proxy)) item.proxyAddress = proxy.address item.onTouch = function() - allowReadAndWriteSwitchAndLabel.switch:setState(MineOSCore.properties.network.users[item.proxyAddress].allowReadAndWrite) + allowReadAndWriteSwitchAndLabel.switch:setState(system.properties.networkUsers[item.proxyAddress].allowReadAndWrite) end end end @@ -63,40 +62,40 @@ module.onTouch = function() end end - application:draw() + workspace:draw() end networkNameInput.onInputFinished = function() - MineOSCore.properties.network.name = #networkNameInput.text > 0 and networkNameInput.text or nil - MineOSCore.saveProperties() - MineOSNetwork.broadcastComputerState(MineOSCore.properties.network.enabled) + system.properties.networkName = #networkNameInput.text > 0 and networkNameInput.text or nil + system.saveProperties() + network.broadcastComputerState(system.properties.networkEnabled) end signalStrengthSlider.onValueChanged = function() - MineOSCore.properties.network.signalStrength = math.floor(signalStrengthSlider.value) - MineOSCore.saveProperties() + system.properties.networkSignalStrength = math.floor(signalStrengthSlider.value) + system.saveProperties() end stateSwitchAndLabel.switch.onStateChanged = function() if stateSwitchAndLabel.switch.state then - MineOSNetwork.enable() + network.enable() else - MineOSNetwork.disable() + network.disable() end check() end allowReadAndWriteSwitchAndLabel.switch.onStateChanged = function() - MineOSCore.properties.network.users[remoteComputersComboBox:getItem(remoteComputersComboBox.selectedItem).proxyAddress].allowReadAndWrite = allowReadAndWriteSwitchAndLabel.switch.state - MineOSCore.saveProperties() + system.properties.networkUsers[remoteComputersComboBox:getItem(remoteComputersComboBox.selectedItem).proxyAddress].allowReadAndWrite = allowReadAndWriteSwitchAndLabel.switch.state + system.saveProperties() end -- Empty object-listener - emptyObject.eventHandler = function(application, object, e1, e2, e3, ...) + emptyObject.eventHandler = function(workspace, object, e1, e2, e3, ...) if (e1 == "component_added" or e1 == "component_removed") and e3 == "modem" then check() - elseif e1 == "MineOSNetwork" and e2 == "updateProxyList" then + elseif e1 == "network" and e2 == "updateProxyList" then check() end end diff --git a/Applications/Settings/Modules/6_Localizations/Icon.pic b/Applications/Settings.app/Modules/6_Localizations/Icon.pic similarity index 100% rename from Applications/Settings/Modules/6_Localizations/Icon.pic rename to Applications/Settings.app/Modules/6_Localizations/Icon.pic diff --git a/Applications/Settings.app/Modules/6_Localizations/Main.lua b/Applications/Settings.app/Modules/6_Localizations/Main.lua new file mode 100644 index 00000000..9f55bc1d --- /dev/null +++ b/Applications/Settings.app/Modules/6_Localizations/Main.lua @@ -0,0 +1,42 @@ + +local GUI = require("GUI") +local paths = require("Paths") +local system = require("System") +local filesystem = require("Filesystem") + +local module = {} + +local workspace, window, localization = table.unpack({...}) + +-------------------------------------------------------------------------------- + +module.name = localization.localizations +module.margin = 0 +module.onTouch = function() + local comboBox = window.contentLayout:addChild(GUI.comboBox(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xD2D2D2, 0xA5A5A5)) + window.contentLayout:addChild(GUI.textBox(1, 1, 36, 1, nil, 0xA5A5A5, {localization.localizationsInfo}, 1, 0, 0, true, true)) + + local list = filesystem.list(paths.system.localizations) + for i = 1, #list do + local name = filesystem.hideExtension(list[i]) + comboBox:addItem(name).onTouch = function() + system.properties.localizationLanguage = name + system.localization = system.getLocalization(paths.system.localizations) + + system.updateWorkspace() + system.updateDesktop() + workspace:draw() + + system.saveProperties() + end + + if name == system.properties.localizationLanguage then + comboBox.selectedItem = comboBox:count() + end + end +end + +-------------------------------------------------------------------------------- + +return module + diff --git a/Applications/Settings/Modules/7_Time/Icon.pic b/Applications/Settings.app/Modules/7_Time/Icon.pic similarity index 100% rename from Applications/Settings/Modules/7_Time/Icon.pic rename to Applications/Settings.app/Modules/7_Time/Icon.pic diff --git a/Applications/Settings/Modules/7_Time/Main.lua b/Applications/Settings.app/Modules/7_Time/Main.lua similarity index 61% rename from Applications/Settings/Modules/7_Time/Main.lua rename to Applications/Settings.app/Modules/7_Time/Main.lua index d1ad76df..b619316b 100644 --- a/Applications/Settings/Modules/7_Time/Main.lua +++ b/Applications/Settings.app/Modules/7_Time/Main.lua @@ -1,12 +1,11 @@ local GUI = require("GUI") -local MineOSInterface = require("MineOSInterface") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") +local paths = require("Paths") +local system = require("System") local module = {} -local application, window, localization = table.unpack({...}) +local workspace, window, localization = table.unpack({...}) -------------------------------------------------------------------------------- @@ -24,23 +23,22 @@ module.onTouch = function() window.contentLayout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.timeFormat)) - local input = window.contentLayout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xA5A5A5, 0xE1E1E1, 0x2D2D2D, MineOSCore.properties.dateFormat or "")) + local input = window.contentLayout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xA5A5A5, 0xE1E1E1, 0x2D2D2D, system.properties.timeFormat or "")) - local switch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.timeUseRealTimestamp .. ":", MineOSCore.properties.timeUseRealTimestamp)).switch + local switch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.timeUseRealTimestamp .. ":", system.properties.timeRealTimestamp)).switch window.contentLayout:addChild(GUI.textBox(1, 1, 36, 1, nil, 0xA5A5A5, {localization.timeInfo}, 1, 0, 0, true, true)) - comboBox.selectedItem = (MineOSCore.properties.timezone or 0) + 13 + comboBox.selectedItem = (system.properties.timeTimezone or 0) + 13 comboBox.onItemSelected = function() - MineOSCore.properties.timeUseRealTimestamp = switch.state - MineOSCore.properties.timezone = comboBox.selectedItem - 13 - MineOSCore.properties.dateFormat = input.text + system.properties.timeRealTimestamp = switch.state + system.properties.timeTimezone = (comboBox.selectedItem - 13) * 3600 + system.properties.timeFormat = input.text + + system.updateMenuWidgets() + workspace:draw() - MineOSCore.updateTimezone() - MineOSCore.updateTime() - MineOSInterface.application:draw() - - MineOSCore.saveProperties() + system.saveProperties() end input.onInputFinished, switch.onStateChanged = comboBox.onItemSelected, comboBox.onItemSelected diff --git a/Applications/Settings/Modules/8_System/Icon.pic b/Applications/Settings.app/Modules/8_System/Icon.pic similarity index 100% rename from Applications/Settings/Modules/8_System/Icon.pic rename to Applications/Settings.app/Modules/8_System/Icon.pic diff --git a/Applications/Settings/Modules/8_System/Main.lua b/Applications/Settings.app/Modules/8_System/Main.lua similarity index 77% rename from Applications/Settings/Modules/8_System/Main.lua rename to Applications/Settings.app/Modules/8_System/Main.lua index 7cfcb5e6..59441ec5 100644 --- a/Applications/Settings/Modules/8_System/Main.lua +++ b/Applications/Settings.app/Modules/8_System/Main.lua @@ -1,14 +1,11 @@ local GUI = require("GUI") -local MineOSInterface = require("MineOSInterface") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") -local computer = require("computer") -local unicode = require("unicode") +local paths = require("Paths") +local system = require("System") local module = {} -local application, window, localization = table.unpack({...}) +local workspace, window, localization = table.unpack({...}) -------------------------------------------------------------------------------- @@ -50,7 +47,7 @@ module.onTouch = function() RAMComboBox:addItem(libraries[i]) end - MineOSInterface.application:draw() + workspace:draw() end window.contentLayout:addChild(GUI.button(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x696969, 0xE1E1E1, localization.systemUnload)).onTouch = function() @@ -58,18 +55,18 @@ module.onTouch = function() update() end - local switch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.systemUnloading .. ":", MineOSCore.properties.packageUnloading)).switch + local switch = window.contentLayout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0xE1E1E1, 0xFFFFFF, 0xA5A5A5, localization.systemUnloading .. ":", system.properties.packageUnloading)).switch switch.onStateChanged = function() - MineOSCore.properties.packageUnloading = switch.state - MineOSCore.setPackageUnloading(MineOSCore.properties.packageUnloading) - MineOSCore.saveProperties() + system.properties.packageUnloading = switch.state + system.setPackageUnloading(system.properties.packageUnloading) + system.saveProperties() end window.contentLayout:addChild(GUI.textBox(1, 1, 36, 1, nil, 0xA5A5A5, {localization.systemInfo}, 1, 0, 0, true, true)) update() - MineOSInterface.application:draw() + workspace:draw() end -------------------------------------------------------------------------------- diff --git a/Applications/Settings/Modules/00_Users/Main.lua b/Applications/Settings/Modules/00_Users/Main.lua deleted file mode 100644 index cc70da2a..00000000 --- a/Applications/Settings/Modules/00_Users/Main.lua +++ /dev/null @@ -1,72 +0,0 @@ - -local GUI = require("GUI") -local computer = require("computer") - -local module = {} - -local application, window, localization = table.unpack({...}) - --------------------------------------------------------------------------------- - -module.name = localization.users -module.margin = 0 -module.onTouch = function() - window.contentLayout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.usersAdd)) - - local input = window.contentLayout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xA5A5A5, 0xE1E1E1, 0x2D2D2D, "", localization.usersTypeNameHere)) - - window.contentLayout:addChild(GUI.textBox(1, 1, 36, 1, nil, 0xA5A5A5, {localization.usersInfo}, 1, 0, 0, true, true)) - - local usersListText = window.contentLayout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.usersList)) - - local usersLayout = window.contentLayout:addChild(GUI.layout(1, 1, 36, 1, 1, 1)) - usersLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP) - usersLayout:setSpacing(1, 1, 0) - - local function update() - local users = {computer.users()} - -- local users = {"ECS", "Xylic", "Computrix", "Yan0t", "Кукарек", "Bird", "Pirnogion"} - - usersLayout:removeChildren() - usersLayout.height = 0 - - usersListText.hidden = #users == 0 - usersLayout.hidden = usersListText.hidden - - module.margin = #users * 3 - - if #users > 0 then - local step = false - - for i = 1, #users do - local userContainer = usersLayout:addChild(GUI.container(1, 1, usersLayout.width, 3)) - userContainer:addChild(GUI.panel(1, 1, userContainer.width - 5, userContainer.height, 0xE1E1E1)) - userContainer:addChild(GUI.text(2, 2, 0x696969, string.limit(users[i], userContainer.width - 5, "right"))) - userContainer:addChild(GUI.button(userContainer.width - 4, 1, 5, 3, step and 0xC3C3C3 or 0xD2D2D2, 0x0, 0x969696, 0xE1E1E1, "x")).onTouch = function() - computer.removeUser(users[i]) - - update() - application:draw() - end - - usersLayout.height, step = usersLayout.height + userContainer.height, not step - end - end - end - - input.onInputFinished = function() - if #input.text > 0 then - computer.addUser(input.text) - input.text = "" - - update() - application:draw() - end - end - - update() -end - --------------------------------------------------------------------------------- - -return module diff --git a/Applications/Settings/Modules/6_Localizations/Main.lua b/Applications/Settings/Modules/6_Localizations/Main.lua deleted file mode 100644 index 56d031dc..00000000 --- a/Applications/Settings/Modules/6_Localizations/Main.lua +++ /dev/null @@ -1,44 +0,0 @@ - -local GUI = require("GUI") -local MineOSInterface = require("MineOSInterface") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") -local filesystem = require("filesystem") - -local module = {} - -local application, window, localization = table.unpack({...}) - --------------------------------------------------------------------------------- - -module.name = localization.localizations -module.margin = 0 -module.onTouch = function() - local comboBox = window.contentLayout:addChild(GUI.comboBox(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0xD2D2D2, 0xA5A5A5)) - window.contentLayout:addChild(GUI.textBox(1, 1, 36, 1, nil, 0xA5A5A5, {localization.localizationsInfo}, 1, 0, 0, true, true)) - - for file in filesystem.list(MineOSPaths.localizationFiles) do - local name = filesystem.hideExtension(file) - comboBox:addItem(name).onTouch = function() - MineOSCore.properties.language = name - MineOSCore.localization = MineOSCore.getLocalization(MineOSPaths.localizationFiles) - - MineOSInterface.createWidgets() - MineOSInterface.changeResolution() - MineOSInterface.changeWallpaper() - MineOSCore.updateTime() - MineOSInterface.updateFileListAndDraw() - - MineOSCore.saveProperties() - end - - if name == MineOSCore.properties.language then - comboBox.selectedItem = comboBox:count() - end - end -end - --------------------------------------------------------------------------------- - -return module - diff --git a/Applications/Shooting/Icon.pic b/Applications/Shooting/Icon.pic deleted file mode 100644 index 7a2f264ad7ba784dc5eca7726c2c5e3091702a3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138 zcmXYou?>JQ3(=10 then - drawSymb(x, y, tostring(math.floor(text/10)), color) - drawSymb(x + 6, y, tostring(math.floor(text%10)), color) - else - drawSymb(x, y, tostring(text), color) - end -end - -local function isIn(x1, y1, x2, y2, xClick, yClick) - if xClick >= x1 and xClick <= x2 and yClick >=y1 and yClick <= y2 then - return true - else - return false - end -end - -local function drawLastScore(x, y, score, color) - ecs.square((x + 6) * 2, y, 35, 7, 0x262626) - drawKrug(x + 3, y + 3, 3, color) - drawText(x + 9, y, score, 0xffffff) - - local yPos = 34 - newObj("Buttons", "Заново", ecs.drawAdaptiveButton(xScore, yPos, 18, 1, "Заново", ecs.colors.blue, 0xffffff)) - yPos = yPos + 4 - newObj("Buttons", "Выйти ", ecs.drawAdaptiveButton(xScore, yPos, 18, 1, "Выйти ", ecs.colors.blue, 0xffffff)) - -end - -local function Tir() - ecs.prepareToExit() - - showPlayers(xScore, yScore) - drawLastScore(xScore / 2, 22, 0, 0xffffff) - - drawMishen() - while true do - local e = {event.pull()} - if e[1] == "touch" then - for key, val in pairs(obj["Buttons"]) do - if ecs.clickedAtArea(e[3], e[4], obj["Buttons"][key][1], obj["Buttons"][key][2], obj["Buttons"][key][3], obj["Buttons"][key][4]) then - ecs.drawAdaptiveButton(obj["Buttons"][key][1], obj["Buttons"][key][2], 18, 1, key, 0xffffff, 0x000000) - os.sleep(0.5) - - if key == "Выйти " then - return "exit" - else - players = {} - return 0 - end - end - end - e[3] = e[3]/2 - AddPlayer(e[6]) - AddScore(e[6], GetScore(e[3], e[4])) - SetPixel(e[3], e[4], players[e[6]][2]) - showPlayers(xScore, yScore) - drawLastScore(xScore / 2, 22, GetScore(e[3], e[4]),players[e[6]][2]) - end - end -end - --------------------------- - -gpu.setBackground(0x262626) -gpu.fill(1, 1, xSize, ySize, " ") - -while true do - local exit = Tir() - if exit == "exit" then break end -end - -gpu.setResolution(xOld, yOld) -ecs.prepareToExit() - diff --git a/Applications/SmartHouse/Icon.pic b/Applications/SmartHouse/Icon.pic deleted file mode 100644 index 6ff36b49bfc31efa9addc492e4725f267050ceec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 115 zcmW-XyA6Oa5JO|%;NPrs4>}-gD!F1{h;r ue=tw)d00aacgB==ZkidZH07HYsI-8tYwo;9&=clm5s@fWZhzB6Q1b!njtx=( diff --git a/Applications/SmartHouse/Modules/command_block/Icon.pic b/Applications/SmartHouse/Modules/command_block/Icon.pic deleted file mode 100644 index c8c426879e430ada131cff7ca64dd52b197f44fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 365 zcmXX?J5I$w49xhm-cORv5x5Hy5*24i%^luTBnlc@4naC9YSM6lcLA=DDjnX4#$J2I zp7B22KR$#hqU-3+F6a<2+|Y?Y70S>6JsC zVbG*N44|0uv^XpnR&*>9V(#^f;f%lUFZDt*4CR1P?^`y4P|TAOO;_3-N}GrLMJowR zk$zmRJY)5><%8Rth6UU-SW+(ir(*&3))J-i?O9ASo{N3`Y1>M diff --git a/Applications/SmartHouse/Modules/command_block/Main.lua b/Applications/SmartHouse/Modules/command_block/Main.lua deleted file mode 100755 index ea2a9b28..00000000 --- a/Applications/SmartHouse/Modules/command_block/Main.lua +++ /dev/null @@ -1,50 +0,0 @@ - -local GUI = require("GUI") - -local module = { - allowSignalConnections = true, - updateWhenModuleDetailsIsHidden = false, -} - -------------------------------------------------------------------------------------------------------------------------------------------------------- - -local function execute(moduleContainer, command) - moduleContainer.componentProxy.setCommand(command) - moduleContainer.componentProxy.executeCommand() -end - --- This method is called once during module initialization -function module.start(moduleContainer) - local x, y = 2, moduleContainer.children[#moduleContainer.children].localPosition.y + 2 - - moduleContainer.commandTextBox = moduleContainer:addInputTextBox(x, y, moduleContainer.width - 2, 1, nil, 0xDDDDDD, nil, 0xFFFFFF, "/say Hello", "Type command here", false, false) - y = y + 2 - moduleContainer.executeButton = moduleContainer:addButton(2, y, moduleContainer.width - 2, 1, 0xDDDDDD, 0x262626, 0xAAAAAA, 0x262626, "Execute") - moduleContainer.executeButton.onTouch = function() - execute(moduleContainer, moduleContainer.commandTextBox.text) - end -end - --- This method is called on each frame update (every second by default), but only if module details is not hidden or updateWhenModuleDetailsIsHidden == true -function module.update(moduleContainer, eventData) - -end - --- This method is called when a this module receives virtual signal from the another module, but only if field allowSignalConnections == true -function module.onSignalReceived(moduleContainer, ...) - local data = {...} - execute(moduleContainer, moduleContainer.commandTextBox.text) -end - -------------------------------------------------------------------------------------------------------------------------------------------------------- - -return module - - - - - - - - - diff --git a/Applications/SmartHouse/Modules/homePC/Icon.pic b/Applications/SmartHouse/Modules/homePC/Icon.pic deleted file mode 100644 index 66ce736059671645c2b8559bce748c1d76fc4218..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299 zcmXYs%Mrpb3`D*9*-n6@1g=RN%rL`=L!2t;A-PZk1yTvJ!(ms}&eKSK-X3=w6GEVC z+z^OCLev!mM6U}13t3hdyLQhVkZxH0rA|x;351bc>=Hptqc*qZv3Y7fBN%}hEMNs2 zI5J!Qe%?B3A=CbJz6|jwi{Bu+LXUPr>RHrs*PspZ$F#sIST9p`&E8v{z!cL4PNK)i jic0#?G6UcpXn%PDYB8P8#RJX#rO*z_HOqCCF9!VpKa&zX diff --git a/Applications/SmartHouse/Modules/homePC/Main.lua b/Applications/SmartHouse/Modules/homePC/Main.lua deleted file mode 100755 index a2f51fd9..00000000 --- a/Applications/SmartHouse/Modules/homePC/Main.lua +++ /dev/null @@ -1,45 +0,0 @@ - -local GUI = require("GUI") - -local module = { - allowSignalConnections = false, - updateWhenModuleDetailsIsHidden = false, -} - -------------------------------------------------------------------------------------------------------------------------------------------------------- - --- This method is called once during module initialization -function module.start(moduleContainer) - local x, y = 2, moduleContainer.children[#moduleContainer.children].localPosition.y + 2 - - moduleContainer:addButton(2, y, moduleContainer.width - 2, 1, 0xDDDDDD, 0x262626, 0xAAAAAA, 0x262626, "Shutdown").onTouch = function() - require("computer").shutdown() - end - y = y + 2 - moduleContainer:addButton(2, y, moduleContainer.width - 2, 1, 0xDDDDDD, 0x262626, 0xAAAAAA, 0x262626, "Reboot").onTouch = function() - require("computer").shutdown(true) - end -end - --- This method is called on each frame update (every second by default), but only if module details is not hidden or updateWhenModuleDetailsIsHidden == true -function module.update(moduleContainer, eventData) - -end - --- This method is called when a this module receives virtual signal from the another module, but only if field allowSignalConnections == true -function module.onSignalReceived(moduleContainer, ...) - -end - -------------------------------------------------------------------------------------------------------------------------------------------------------- - -return module - - - - - - - - - diff --git a/Applications/SmartHouse/Modules/homePC/Server.pic b/Applications/SmartHouse/Modules/homePC/Server.pic deleted file mode 100644 index 4fad2e85c8cb17e7d8308fd3b5391bb8d42e8db3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 237 zcmZ9`F%E+;3M^5_sr z`}R{oW9pgIa`EnVf7E`+mv>Vkow>xgAM8vE%%bSs3LarjxO9nXs4D~tn<-S(g4*Dh l#eMrixI#6lcMHmKabklwh>;-$4Hj}J!GlCq&wrIO{Q+Fg4Uhl; diff --git a/Applications/SmartHouse/Modules/mfsu/Icon.pic b/Applications/SmartHouse/Modules/mfsu/Icon.pic deleted file mode 100644 index 222dc330b71bea76c01d6b04f6c5b7be0777915d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 295 zcmYk1yG;c#5Ji3G=drz?R|35tArVp2N=PKc6oJTG0{Ld^0$wG|zD)>@G}js*U%x%S zzSzx?5oF#jy8!Rq zF=BE<8{CyDh11$JFDtjl?BxvN#FpE%tO|GXatLwu%4l->$__^TM2CX9Zt%;L&&xxA tlK-idY_TiXb76-P>o*+jfU->U4@L;{FCwFC=!C0IbU5q8w>}<^t$)C!9##MV diff --git a/Applications/SmartHouse/Modules/mfsu/Main.lua b/Applications/SmartHouse/Modules/mfsu/Main.lua deleted file mode 100755 index 57d7fe5f..00000000 --- a/Applications/SmartHouse/Modules/mfsu/Main.lua +++ /dev/null @@ -1,43 +0,0 @@ - -local GUI = require("GUI") - -local module = { - allowSignalConnections = false, - updateWhenModuleDetailsIsHidden = false, -} - -------------------------------------------------------------------------------------------------------------------------------------------------------- - --- This method is called once during module initialization -function module.start(moduleContainer) - local x, y = 2, moduleContainer.children[#moduleContainer.children].localPosition.y + 2 - - moduleContainer.capaticyLabel = moduleContainer:addLabel(x, y, moduleContainer.width - 2, 1, 0xDDDDDD, "Capacity"):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top); y = y + 1 - moduleContainer.chart = moduleContainer:addChart(x, y, moduleContainer.width - 2, math.floor(moduleContainer.width - 2) / 2, 0xFFFFFF, 0x999999, 0xFFDB40, "t", "%", 0, 100, {}) -end - --- This method is called on each frame update (every second by default), but only if module details is not hidden or updateWhenModuleDetailsIsHidden == true -function module.update(moduleContainer, eventData) - table.insert(moduleContainer.chart.values, math.ceil(moduleContainer.componentProxy.getStored() / moduleContainer.componentProxy.getCapacity() * 100)) - if #moduleContainer.chart.values > 100 then - table.remove(moduleContainer.chart.values, 1) - end -end - --- This method is called when a this module receives virtual signal from the another module, but only if field allowSignalConnections == true -function module.onSignalReceived(moduleContainer, ...) - -end - -------------------------------------------------------------------------------------------------------------------------------------------------------- - -return module - - - - - - - - - diff --git a/Applications/SmartHouse/Modules/motion_sensor/Icon.pic b/Applications/SmartHouse/Modules/motion_sensor/Icon.pic deleted file mode 100644 index b34c81578f068cde0882d66c9c4c83acb8505349..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 277 zcmZ9EyA8uI3`BJ#Mae(eBc!ynMt}gRQrKR^jTgua88DNCwz?F&NAQUEx<4N}ML0({ zUE#n)<6`v2%}B->E`l%-gC{;i&hTfb-GfcE$#5b_9!Ze`aVjPVCis5d;h{>sCu%aEbq Nj6#IONGleI34g;f5Ay&3 diff --git a/Applications/SmartHouse/Modules/motion_sensor/Main.lua b/Applications/SmartHouse/Modules/motion_sensor/Main.lua deleted file mode 100755 index d517bf57..00000000 --- a/Applications/SmartHouse/Modules/motion_sensor/Main.lua +++ /dev/null @@ -1,53 +0,0 @@ - -local GUI = require("GUI") - -local module = { - allowSignalConnections = false, - updateWhenModuleDetailsIsHidden = true, -} - -------------------------------------------------------------------------------------------------------------------------------------------------------- - -local sleepValue = 1 - --- This method is called once during module initialization -function module.start(moduleContainer) - local x, y = 1, moduleContainer.children[#moduleContainer.children].localPosition.y + 2 - - local lines = {limit = 5} - moduleContainer.editWhitelistButton = moduleContainer:addButton(2, y, moduleContainer.width - 2, 1, 0xDDDDDD, 0x262626, 0xAAAAAA, 0x262626, "Whitelist") - y = y + 2 - moduleContainer.sleepSlider = moduleContainer:addHorizontalSlider(x, y, moduleContainer.width, 0xFFDB80, 0x000000, 0xFFDB40, 0xDDDDDD, 0.5, 2, sleepValue, false, "Sleep: ") - moduleContainer.sleepSlider.onValueChanged = function() - sleepValue = moduleContainer.sleepSlider.value - end -end - --- This method is called on each frame update (every second by default), but only if module details is not hidden or updateWhenModuleDetailsIsHidden == true -function module.update(moduleContainer, eventData) - if eventData[1] == "motion" then - if moduleContainer.componentProxy.address == eventData[2] then - if eventData[6] == "ECS" then - moduleContainer:sendSignal("redstone", "pulse") - end - end - end -end - --- This method is called when a this module receives virtual signal from the another module, but only if field allowSignalConnections == true -function module.onSignalReceived(moduleContainer, ...) - -end - -------------------------------------------------------------------------------------------------------------------------------------------------------- - -return module - - - - - - - - - diff --git a/Applications/SmartHouse/Modules/reactor/Icon.pic b/Applications/SmartHouse/Modules/reactor/Icon.pic deleted file mode 100644 index fa84a3ea29755f4fffc6b4b760be454aafa5c2c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 275 zcmYk0%W1?w5Jab{KQsC(ggq*cg$17+lfW@J3Z*j%I1TyW)|dqy9yE0I>-v0tynHQ4 zi4Q%IjG9Z=3rnZWy>()q`(d1BSG954TEu)9r5Angv|RIl>N?L&QjwJ8k^>1`Df6-; z6%Z=}2{f?4gB+CL=ljb~P0*-Ok!j;E47$PTLzRQ!4Xw-@NS$_+TbYA3zR7GY)3%pq VNjPCKY`By@>^#suEETxj{{e!67NP(E diff --git a/Applications/SmartHouse/Modules/reactor/Main.lua b/Applications/SmartHouse/Modules/reactor/Main.lua deleted file mode 100755 index 9f2d7405..00000000 --- a/Applications/SmartHouse/Modules/reactor/Main.lua +++ /dev/null @@ -1,40 +0,0 @@ - -local GUI = require("GUI") - -local module = { - allowSignalConnections = false, - updateWhenModuleDetailsIsHidden = false, -} - -------------------------------------------------------------------------------------------------------------------------------------------------------- - --- This method is called once during module initialization -function module.start(moduleContainer) - local x, y = 2, moduleContainer.children[#moduleContainer.children].localPosition.y + 2 - - moduleContainer.heatLabel = moduleContainer:addLabel(x, y, moduleContainer.width - 2, 1, 0xDDDDDD, ""); y = y + 1 - moduleContainer.outputLabel = moduleContainer:addLabel(x, y, moduleContainer.width - 2, 1, 0xDDDDDD, "") -end - --- This method is called on each frame update (every second by default), but only if module details is not hidden or updateWhenModuleDetailsIsHidden == true -function module.update(moduleContainer, eventData) - moduleContainer.heatLabel.text = "Heat: " .. math.ceil(moduleContainer.componentProxy.getHeat() / moduleContainer.componentProxy.getMaxHeat() * 100) .. "%" - moduleContainer.outputLabel.text = "Output: " .. moduleContainer.componentProxy.getReactorEnergyOutput() -end - --- This method is called when a this module receives virtual signal from the another module, but only if field allowSignalConnections == true -function module.onSignalReceived(moduleContainer, ...) - -end - -------------------------------------------------------------------------------------------------------------------------------------------------------- - -return module - - - - - - - - diff --git a/Applications/SmartHouse/Modules/redstone/Icon.pic b/Applications/SmartHouse/Modules/redstone/Icon.pic deleted file mode 100644 index 73593d92bb76b0fc82125d7da0f48930efe89926..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 359 zcmW+x$xQ@75No@RKIRO8=j^6|gv5)-377>) zZHsUG!2zT7+sV~O?=TE2^-MF!{#dBp2M;Lf4nw)=9S%E*{v_IOjP-KXFe#B2MO3{` k3mH8muyTvVDT=6A=?=ZcIbIy)Gx)s6l&jM6odR?8|5MQ&ng9R* diff --git a/Applications/SmartHouse/Modules/redstone/Main.lua b/Applications/SmartHouse/Modules/redstone/Main.lua deleted file mode 100755 index b6112dd2..00000000 --- a/Applications/SmartHouse/Modules/redstone/Main.lua +++ /dev/null @@ -1,92 +0,0 @@ - -local GUI = require("GUI") -local sides = require("sides") - -local module = { - allowSignalConnections = true, - updateWhenModuleDetailsIsHidden = false, -} - -------------------------------------------------------------------------------------------------------------------------------------------------------- - -local function changeRedstoneState(moduleContainer, state) - local sidesThatWillBeChanged = {} - local comboBoxText = moduleContainer.sidesComboBox.items[moduleContainer.sidesComboBox.currentItem].text - if comboBoxText == "All" then - sidesThatWillBeChanged = {0,1,2,3,4,5} - -- ecs.error("ALLL SIDES YOPTA") - else - sidesThatWillBeChanged = {sides[string.lower(comboBoxText)]} - -- ecs.error("HERE HERE: " .. sides[string.lower(comboBoxText)]) - end - - for i = 1, #sidesThatWillBeChanged do - moduleContainer.redstoneStates[sidesThatWillBeChanged[i]] = state - moduleContainer.componentProxy.setOutput(sidesThatWillBeChanged[i], state and 15 or 0) - end -end - --- This method is called once during module initialization -function module.start(moduleContainer) - local x, y = 2, moduleContainer.children[#moduleContainer.children].localPosition.y + 2 - - moduleContainer.redstoneStates = {} - for i = 0, 5 do - local signalStrength = moduleContainer.componentProxy.getOutput(i) - moduleContainer.redstoneStates[i] = signalStrength > 0 and true or false - end - - moduleContainer:addLabel(x, y, moduleContainer.width - 2, 1, 0xDDDDDD, "Signal:") - moduleContainer.signalSwitch = moduleContainer:addSwitch(moduleContainer.width - 6, y, 6, 0xFFDB40, 0xBBBBBB, 0xFFFFFF, false) - moduleContainer.signalSwitch.onStateChanged = function() - changeRedstoneState(moduleContainer, moduleContainer.signalSwitch.state) - end - y = y + 2 - moduleContainer.emitOnceButton = moduleContainer:addButton(2, y, moduleContainer.width - 2, 1, 0xDDDDDD, 0x262626, 0xAAAAAA, 0x262626, "Emit once") - moduleContainer.emitOnceButton.onTouch = function() - changeRedstoneState(moduleContainer, true) - os.sleep(0.1) - changeRedstoneState(moduleContainer, false) - moduleContainer.signalSwitch.state = false - end - y = y + 2 - moduleContainer:addLabel(x, y, moduleContainer.width - 2, 1, 0xFFFFFF, "Side"):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top) - y = y + 2 - moduleContainer.sidesComboBox = moduleContainer:addComboBox(x, y, moduleContainer.width - 2, 1, 0xDDDDDD, 0x262626, 0xCCCCCC, 0x262626, {"All", "Up", "Down", "North", "South", "West", "East"}) - moduleContainer.sidesComboBox.onItemSelected = function() - local comboBoxText = moduleContainer.sidesComboBox.items[moduleContainer.sidesComboBox.currentItem].text - if comboBoxText == "All" then - moduleContainer.signalSwitch.state = false - else - local side = sides[string.lower(comboBoxText)] - moduleContainer.signalSwitch.state = moduleContainer.redstoneStates[side] - end - end - y = y + 2 -end - --- This method is called on each frame update (every second by default), but only if module details is not hidden or updateWhenModuleDetailsIsHidden == true -function module.update(moduleContainer, eventData) - -end - --- This method is called when a this module receives virtual signal from the another module, but only if field allowSignalConnections == true -function module.onSignalReceived(moduleContainer, ...) - local data = {...} - if data[1] == "redstone" and data[2] == "pulse" then - changeRedstoneState(moduleContainer, data[3]) - end -end - -------------------------------------------------------------------------------------------------------------------------------------------------------- - -return module - - - - - - - - - diff --git a/Applications/SmartHouse/Modules/screen/Icon.pic b/Applications/SmartHouse/Modules/screen/Icon.pic deleted file mode 100644 index e1b5f8ac45ae1e5e439f7b013c0738e85b0dd89c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 231 zcmZ9GI}XAy5JY#z_S!&5as*1^Yak&}QAD{;(r^Y2$Vr%Vm}0bk`_c3B_PG04V8)AA zSct@!luQn@7V3LNew~( diff --git a/Applications/SmartHouse/Modules/screen/Main.lua b/Applications/SmartHouse/Modules/screen/Main.lua deleted file mode 100755 index d14a0ae4..00000000 --- a/Applications/SmartHouse/Modules/screen/Main.lua +++ /dev/null @@ -1,37 +0,0 @@ - -local GUI = require("GUI") - -local module = { - allowSignalConnections = false, - updateWhenModuleDetailsIsHidden = false, -} - -------------------------------------------------------------------------------------------------------------------------------------------------------- - --- This method is called once during module initialization -function module.start(moduleContainer) - -end - --- This method is called on each frame update (every second by default), but only if module details is not hidden or updateWhenModuleDetailsIsHidden == true -function module.update(moduleContainer, eventData) - -end - --- This method is called when a this module receives virtual signal from the another module, but only if field allowSignalConnections == true -function module.onSignalReceived(moduleContainer, ...) - -end - -------------------------------------------------------------------------------------------------------------------------------------------------------- - -return module - - - - - - - - - diff --git a/Applications/SmartHouse/SmartHouse.lua b/Applications/SmartHouse/SmartHouse.lua deleted file mode 100755 index ee180e7d..00000000 --- a/Applications/SmartHouse/SmartHouse.lua +++ /dev/null @@ -1,494 +0,0 @@ - -local sides = require("sides") -local component = require("component") -local advancedLua = require("advancedLua") -local image = require("image") -local buffer = require("doubleBuffering") -local keyboard = require("keyboard") -local GUI = require("GUI") -local ecs = require("ECSAPI") -local MineOSCore = require("MineOSCore") -local computer = require("computer") -local fs = require("filesystem") - ------------------------------------------------------------------------------------------------------------------------------------ - -local window - -local paths = {} -paths.resources = MineOSCore.getCurrentApplicationResourcesDirectory() --- paths.resources = "/SmartHouse/" -paths.modules = paths.resources .. "Modules/" - -local colors = { - -- background = image.load("/MineOS/Pictures/Ciri.pic"), - background = 0x1b1b1b, - connectionLines = 0xFFFFFF, - devicesBackgroundTransparency = 40, - devicesBackground = 0x0, - devicesButtonBackground = 0xFFFFFF, - devicesButtonText = 0x262626, - devicesInfoText = 0xDDDDDD, - groupsTransparency = nil, -} - -local signals = {} -local groups = {} -local modules = {} -local offset = {x = 0, y = 0} - ------------------------------------------------------------------------------------------------------------------------------------ - -local function loadModule(modulePath) - local success, module = pcall(loadfile(modulePath .. "/Main.lua")) - if success then - module.modulePath = modulePath - module.icon = image.load(module.modulePath .. "/Icon.pic") - modules[fs.name(module.modulePath)] = module - else - error("Module loading failed: " .. module) - end -end - -local function loadModules() - modules = {} - for file in fs.list(paths.modules) do - local modulePath = paths.modules .. file - if fs.isDirectory(modulePath) then - loadModule(modulePath) - end - end -end - -local function highlightDiviceAsSignal(deviceContainer, color) - buffer.square(deviceContainer.x - 1, deviceContainer.y, deviceContainer.width + 2, deviceContainer.height, color) - buffer.text(deviceContainer.x - 1, deviceContainer.y - 1, color, string.rep("▄", deviceContainer.width + 2)) - buffer.text(deviceContainer.x - 1, deviceContainer.y + deviceContainer.height, color, string.rep("▀", deviceContainer.width + 2)) -end - -local function createNewGroup(name, color) - table.insert(groups, { - color = color, - name = name, - devices = {} - }) -end - -local function removeDeviceFromGroup(address) - for groupIndex = 1, #groups do - local deviceIndex = 1 - while deviceIndex <= #groups[groupIndex].devices do - if groups[groupIndex].devices[deviceIndex].componentProxy.address == address then - table.remove(groups[groupIndex].devices, deviceIndex) - deviceIndex = deviceIndex - 1 - if #groups[groupIndex].devices == 0 then - groups[groupIndex] = nil - return - end - end - deviceIndex = deviceIndex + 1 - end - end -end - -local function addDeviceToGroup(deviceContainer, name, color) - local groupIndex - - for i = 1, #groups do - if groups[i].name == name then groupIndex = i; break end - end - - if not groupIndex then - createNewGroup(name, color) - groupIndex = #groups - end - - table.insert(groups[groupIndex].devices, deviceContainer) - groups[groupIndex].color = color -end - -local function containerPushSignal(container, ...) - for signalIndex = 1, #signals do - if signals[signalIndex].fromDevice == container then - for toDeviceIndex = 1, #signals[signalIndex].toDevices do - if signals[signalIndex].toDevices[toDeviceIndex].module.onSignalReceived then - signals[signalIndex].toDevices[toDeviceIndex].module.onSignalReceived(signals[signalIndex].toDevices[toDeviceIndex], ...) - end - end - end - end -end - -local function changeChildrenState(container, state) - for i = 3, #container.children do - container.children[i].isHidden = state - end -end - -local function createDevice(x, y, componentName, componentProxy, name) - if not modules[componentName] then error("No such module: " .. componentName) end - local container = window:addContainer(x, y, 16, 9) - - container.name = name - container.module = modules[componentName] - container.componentProxy = componentProxy - container.componentName = componentName - container.detailsIsHidden = true - - x, y = 1, 1 - container.deviceImage = container:addImage(x, y, container.module.icon); y = y + 8 - local stateButton = container:addButton(1, y, container.width, 1, colors.devicesButtonBackground, colors.devicesButtonText, colors.devicesButtonText, colors.devicesButtonBackground, "*") - stateButton.onTouch = function() - container.detailsIsHidden = not container.detailsIsHidden - changeChildrenState(container, container.detailsIsHidden) - if container.detailsIsHidden then - stateButton.localPosition.y = container.backgroundPanel.localPosition.y - else - stateButton.localPosition.y = container.backgroundPanel.localPosition.y + container.backgroundPanel.height - end - end - - container.backgroundPanel = container:addPanel(1, y, container.width, 1, colors.devicesBackground, colors.devicesBackgroundTransparency) - - - container:addLabel(2, y, container.width - 2, 1, 0xFFFFFF, container.name):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top); y = y + 1 - container:addLabel(2, y, container.width - 2, 1, 0x999999, container.componentProxy.address):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top); y = y + 2 - - container.module.start(container) - container.module.update(container, {}) - - y = container.children[#container.children].localPosition.y + (container.children[#container.children].height or 0) + 1 - container.backgroundPanel.height = container.backgroundPanel.height + (y - container.backgroundPanel.y - 1) - - container.deviceImage.onTouch = function(eventData) - if eventData[5] == 0 then - window.deviceToDrag = container - container:moveToFront() - if keyboard.isShiftDown() then - local x, y = container.x + 8, container.y + 4 - signals.fromDevice = container - signals.toPoint = {x = x, y = y} - end - else - local action = GUI.contextMenu(eventData[3], eventData[4], {"Add to group"}, {"Remove from group"}):show() - if action == "Add to group" then - local data = ecs.universalWindow("auto", "auto", 36, 0x262626, true, - {"EmptyLine"}, - {"CenterText", ecs.colors.orange, "Add to group"}, - {"EmptyLine"}, - {"Input", 0xFFFFFF, ecs.colors.orange, "Group #1"}, - {"Color", "Group color", 0xAAAAAA}, - {"EmptyLine"}, - {"Button", {ecs.colors.orange, 0xffffff, "OK"}, {0x999999, 0xffffff, "Cancel"}} - ) - - if data[3] == "OK" then - addDeviceToGroup(container, data[1], data[2]) - end - elseif action == "Remove from group" then - removeDeviceFromGroup(container.componentProxy.address) - end - end - end - - local oldDraw = container.draw - container.draw = function() - if container.highlightColor then - highlightDiviceAsSignal(container, container.highlightColor) - end - oldDraw(container) - end - container.sendSignal = containerPushSignal - - changeChildrenState(container, container.detailsIsHidden) - return container -end - -local function drawConnectionLine(x1, y1, x2, y2, thin, color, cutAfterPixels) - local symbol = "" - if x1 < x2 then - if y1 < y2 then - symbol = thin and "┐" or "█" - else - symbol = thin and "┘" or "▀" - end - else - if y1 < y2 then - symbol = thin and "┌" or "█" - else - symbol = thin and "└" or "▀" - end - end - - local x, y, counter, xIncrement, yIncrement = x1, y1, 1, (x1 <= x2 and 1 or -1), (y1 <= y2 and 1 or -1) - - while true do - local bg = buffer.get(x, y) - buffer.set(x, y, bg, color, thin and "─" or "▀") - x = x + xIncrement - - if counter < cutAfterPixels then - counter = counter + 1 - else - x = x + xIncrement - end - - if x1 <= x2 then - if x >= x2 - 1 then break end - else - if x < x2 + 1 then break end - end - end - - buffer.text(x, y, color, symbol) - y = y + yIncrement - - while true do - local bg = buffer.get(x, y) - buffer.set(x, y, bg, color, thin and "│" or "█") - y = y + yIncrement - - if counter < cutAfterPixels then - counter = counter + 1 - else - y = y + yIncrement - end - - if y1 <= y2 then - if y >= y2 then break end - else - if y < y2 then break end - end - end -end - -local function moveDevice(container, x, y) - container.localPosition.x, container.localPosition.y = container.localPosition.x + x, container.localPosition.y + y -end - -local function moveDevices(x, y) - for i = 1, #window.children do - moveDevice(window.children[i], x, y) - end -end - -local function getGroupGeometry(devices) - local geometry = {} - - for deviceIndex = 1, #devices do - geometry.x = math.min(geometry.x or devices[deviceIndex].localPosition.x, devices[deviceIndex].localPosition.x) - geometry.y = math.min(geometry.y or devices[deviceIndex].localPosition.y, devices[deviceIndex].localPosition.y) - - geometry.xMax = math.max(geometry.xMax or devices[deviceIndex].localPosition.x, devices[deviceIndex].localPosition.x) - geometry.yMax = math.max(geometry.yMax or devices[deviceIndex].localPosition.y, devices[deviceIndex].localPosition.y) - end - - local xOffset, yOffset = 2, 2 - geometry.width, geometry.height = geometry.xMax - geometry.x + 16 + xOffset * 2, geometry.yMax - geometry.y + 9 + yOffset * 2 - geometry.x, geometry.y = geometry.x - xOffset, geometry.y - yOffset - 1 - - return geometry -end - -local function drawGroups() - for groupIndex = 1, #groups do - local groupGeometry = getGroupGeometry(groups[groupIndex].devices) - buffer.square(groupGeometry.x, groupGeometry.y, groupGeometry.width, groupGeometry.height, groups[groupIndex].color) - GUI.label(groupGeometry.x, groupGeometry.y + 1, groupGeometry.width, 1, 0x000000, groups[groupIndex].name):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top):draw() - end -end - -local function drawSignals() - local colorPrimary = 0xFF4940 - local step = 16 - - if signals.fromDevice then - drawConnectionLine( - signals.fromDevice.x + 8, - signals.fromDevice.y + 4, - signals.toPoint.x, - signals.toPoint.y, - false, colorPrimary, step - ) - signals.fromDevice.highlightColor = colorPrimary - end - - for signalIndex = 1, #signals do - --Подсветка подключенных устройств - for toDeviceIndex = 1, #signals[signalIndex].toDevices do - signals[signalIndex].toDevices[toDeviceIndex].highlightColor = colorPrimary - drawConnectionLine( - signals[signalIndex].fromDevice.x + 8, - signals[signalIndex].fromDevice.y + 4, - signals[signalIndex].toDevices[toDeviceIndex].x + 8, - signals[signalIndex].toDevices[toDeviceIndex].y + 4, - false, colorPrimary, step - ) - end - -- Подсветка стартового устройства - signals[signalIndex].fromDevice.highlightColor = colorPrimary - end -end - -local function createWindow() - window = GUI.fullScreenWindow() - - -- Создаем главное и неебически важное устройство домашнего писюка - local homePC = createDevice(math.floor(window.width / 2 - 8), math.floor(window.height / 2 - 4), "homePC", component.proxy(computer.address()), "Сервак") - homePC.module.icon = image.load(homePC.module.modulePath .. "Server.pic") - homePC.deviceImage.image = homePC.module.icon - - -- Перед отрисовкой окна чистим буфер фоном и перехуячиваем позиции объектов групп - window.onDrawStarted = function() - buffer.clear(colors.background) - drawGroups() - - local xPC, yPC = homePC.x + 8, homePC.y + 4 - for i = 1, #window.children do - drawConnectionLine(xPC, yPC, window.children[i].x + 8, window.children[i].y + 4, true, colors.connectionLines, math.huge) - end - - drawSignals() - end - - window.onAnyEvent = function(eventData) - if eventData[1] == "key_down" then - if eventData[4] == 19 then - colors.background = math.random(0x0, 0xFFFFFF) - elseif eventData[4] == 200 then - moveDevices(0, -2) - elseif eventData[4] == 208 then - moveDevices(0, 2) - elseif eventData[4] == 203 then - moveDevices(-4, 0) - elseif eventData[4] == 205 then - moveDevices(4, 0) - end - elseif eventData[1] == "touch" then - window.dragOffset = {x = eventData[3], y = eventData[4]} - elseif eventData[1] == "drag" then - if keyboard.isShiftDown() then - if signals.fromDevice then - signals.toPoint.x, signals.toPoint.y = eventData[3], eventData[4] - end - else - if eventData[5] == 0 and window.deviceToDrag then - window.deviceToDrag.localPosition.x, window.deviceToDrag.localPosition.y = window.deviceToDrag.localPosition.x + (eventData[3] - window.dragOffset.x), window.deviceToDrag.y + (eventData[4] - window.dragOffset.y) - end - end - window.dragOffset = {x = eventData[3], y = eventData[4]} - elseif eventData[1] == "drop" then - if keyboard.isShiftDown() and signals.fromDevice then - --Создаем новый сигнал, если такового еще не существовало - local signalIndex - for i = 1, #signals do - if signals[i].fromDevice == signals.fromDevice then signalIndex = i; break end - end - if not signalIndex then - table.insert(signals, {fromDevice = signals.fromDevice, toDevices = {}}) - signalIndex = #signals - end - - --Ищем контейнер, на который дропнулось - local container - for i = 1, #window.children do - if window.children[i]:isClicked(eventData[3], eventData[4]) then - container = window.children[i] - break - end - end - - -- Если контейнер найден, то - if container then - --Чекаем, принимает ли модуль этого контейнера сигналы, и если принимает, то - if container.module.allowSignalConnections then - --Проверяем, нет ли часом этого устройства в УЖЕ подключенных - local deviceExists = false - for i = 1, #signals[signalIndex].toDevices do - if signals[signalIndex].toDevices[i] == container then deviceExists = true end - end - - --И если нет, а также если это устройство не является устройством, от которого ВЕЛОСЬ подключение, то - if not deviceExists and container ~= signals[signalIndex].fromDevice then - table.insert(signals[signalIndex].toDevices, container) - end - else - GUI.error("This device doesn't support virtual signal receiving", {title = {text = "Warning", color = 0xFF5555}}) - if #signals[signalIndex].toDevices <= 0 then - signals[signalIndex].fromDevice.highlightColor = nil - signals[signalIndex] = nil - end - end - else - -- А если мы дропнули на пустую точку, то оффаем выделение - if #signals[signalIndex].toDevices <= 0 then - signals[signalIndex].fromDevice.highlightColor = nil - signals[signalIndex] = nil - end - end - - end - - - -- Удаляем временные сигнальные переменные - signals.fromDevice, signals.toPoint = nil, nil - -- Сбрасываем также общее смещение драга - window.dragOffset = nil - window.deviceToDrag = nil - end - - for i = 1, #window.children do - if not window.children[i].detailsIsHidden or window.children[i].module.updateWhenModuleDetailsIsHidden then - window.children[i].module.update(window.children[i], eventData) - end - end - - window:draw() - buffer.draw() - end -end - -local function refreshComponents() - local devices = {} - for componentAddress, componentName in pairs(component.list()) do - if modules[componentName] then - table.insert(devices, {componentAddress = componentAddress, componentName = componentName}) - end - end - local x, y = math.floor(buffer.screen.width / 2 - #devices * 18 / 2 + 1), 2 - for i = 1, #devices do - createDevice(x, y, devices[i].componentName, component.proxy(devices[i].componentAddress), devices[i].componentName) - x = x + 18 - end -end - ------------------------------------------------------------------------------------------------------------------------------------ - -buffer.start() - -loadModules() -createWindow() -refreshComponents() -window:draw() -buffer.draw() -window:handleEvents(1) - - - - - - - - - - - - - - - - - - - - diff --git a/Applications/Spinner/1.pic b/Applications/Spinner.app/1.pic similarity index 100% rename from Applications/Spinner/1.pic rename to Applications/Spinner.app/1.pic diff --git a/Applications/Spinner/2.pic b/Applications/Spinner.app/2.pic similarity index 100% rename from Applications/Spinner/2.pic rename to Applications/Spinner.app/2.pic diff --git a/Applications/Spinner/3.pic b/Applications/Spinner.app/3.pic similarity index 100% rename from Applications/Spinner/3.pic rename to Applications/Spinner.app/3.pic diff --git a/Applications/Spinner/4.pic b/Applications/Spinner.app/4.pic similarity index 100% rename from Applications/Spinner/4.pic rename to Applications/Spinner.app/4.pic diff --git a/Applications/Spinner/5.pic b/Applications/Spinner.app/5.pic similarity index 100% rename from Applications/Spinner/5.pic rename to Applications/Spinner.app/5.pic diff --git a/Applications/Spinner/6.pic b/Applications/Spinner.app/6.pic similarity index 100% rename from Applications/Spinner/6.pic rename to Applications/Spinner.app/6.pic diff --git a/Applications/Spinner/7.pic b/Applications/Spinner.app/7.pic similarity index 100% rename from Applications/Spinner/7.pic rename to Applications/Spinner.app/7.pic diff --git a/Applications/Spinner/8.pic b/Applications/Spinner.app/8.pic similarity index 100% rename from Applications/Spinner/8.pic rename to Applications/Spinner.app/8.pic diff --git a/Applications/Spinner/Icon.pic b/Applications/Spinner.app/Icon.pic similarity index 100% rename from Applications/Spinner/Icon.pic rename to Applications/Spinner.app/Icon.pic diff --git a/Applications/Spinner/Main.lua b/Applications/Spinner.app/Main.lua similarity index 70% rename from Applications/Spinner/Main.lua rename to Applications/Spinner.app/Main.lua index 47cb4f41..8f240e66 100644 --- a/Applications/Spinner/Main.lua +++ b/Applications/Spinner.app/Main.lua @@ -1,23 +1,23 @@ -require("advancedLua") -local fs = require("filesystem") -local buffer = require("doubleBuffering") -local color = require("color") -local image = require("image") +local filesystem = require("Filesystem") +local screen = require("Screen") +local color = require("Color") +local image = require("Image") local GUI = require("GUI") +local system = require("System") ------------------------------------------------------------------------------------------ -local spinnersPath = fs.path(getCurrentScript()) +local spinnersPath = filesystem.path(system.getCurrentScript()) local spinners = {} local currentSpinner = 1 local spinnerLimit = 8 local spinnerHue = math.random(0, 360) local spinnerHueStep = 20 -local application = GUI.application() -application:addChild(GUI.panel(1, 1, application.width, application.height, 0x0)) -local spinnerImage = application:addChild(GUI.image(1, 1, {1, 1})) +local workspace = GUI.workspace() +workspace:addChild(GUI.panel(1, 1, workspace.width, workspace.height, 0x0)) +local spinnerImage = workspace:addChild(GUI.image(1, 1, {1, 1})) ------------------------------------------------------------------------------------------ @@ -43,9 +43,9 @@ local function changeColor(hue, saturation) spinnerImage.image = spinners[currentSpinner] end -application.eventHandler = function(application, object, e1, e2, e3, e4, e5) +workspace.eventHandler = function(workspace, object, e1, e2, e3, e4, e5) if e1 == "key_down" then - application:stop() + workspace:stop() elseif e1 == "touch" then spinnerHue = spinnerHue + spinnerHueStep * (e5 == 1 and -1 or 1) if spinnerHue > 360 then @@ -62,7 +62,7 @@ application.eventHandler = function(application, object, e1, e2, e3, e4, e5) end spinnerImage.image = spinners[currentSpinner] - application:draw() + workspace:draw() end ------------------------------------------------------------------------------------------ @@ -72,14 +72,14 @@ for i = 1, spinnerLimit do end spinnerImage.width = image.getWidth(spinners[currentSpinner]) spinnerImage.height = image.getHeight(spinners[currentSpinner]) -spinnerImage.localX = math.floor(application.width / 2 - spinnerImage.width / 2) -spinnerImage.localY = math.floor(application.height / 2 - spinnerImage.height/ 2) +spinnerImage.localX = math.floor(workspace.width / 2 - spinnerImage.width / 2) +spinnerImage.localY = math.floor(workspace.height / 2 - spinnerImage.height/ 2) changeColor(spinnerHue, 1) -buffer.flush() -application:draw() +screen.flush() +workspace:draw() -application:start(0) +workspace:start(0) diff --git a/Applications/Spinner/About/English.txt b/Applications/Spinner/About/English.txt deleted file mode 100644 index c708aaf9..00000000 --- a/Applications/Spinner/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Who needs some fidget spinners? Colorful! \ No newline at end of file diff --git a/Applications/Spinner/About/Russian.txt b/Applications/Spinner/About/Russian.txt deleted file mode 100755 index bd1d7228..00000000 --- a/Applications/Spinner/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Пс-с-ст! Кому немножко спиннеров? \ No newline at end of file diff --git a/Applications/Stargate/Ch1.pic b/Applications/Stargate.app/Ch1.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/Stargate/Ch1.pic rename to Applications/Stargate.app/Ch1.pic diff --git a/Applications/Stargate/Ch2.pic b/Applications/Stargate.app/Ch2.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/Stargate/Ch2.pic rename to Applications/Stargate.app/Ch2.pic diff --git a/Applications/Stargate/Icon.pic b/Applications/Stargate.app/Icon.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/Stargate/Icon.pic rename to Applications/Stargate.app/Icon.pic diff --git a/Applications/Stargate.app/Main.lua b/Applications/Stargate.app/Main.lua new file mode 100755 index 00000000..4bafc4df --- /dev/null +++ b/Applications/Stargate.app/Main.lua @@ -0,0 +1,311 @@ + +local filesystem = require("Filesystem") +local image = require("Image") +local screen = require("Screen") +local GUI = require("GUI") +local paths = require("Paths") +local system = require("System") + +if not component.isAvailable("stargate") then + GUI.alert("This program requires stargate from mod \"SGCraft\"") + return +end + +local stargate = component.get("stargate") + +--------------------------------------------------------------------------------------------- + +local resources = filesystem.path(system.getCurrentScript()) +local pathToContacts = paths.user.applicationData .. "Stargate/Contacts.cfg" +local contacts = {} +local Ch1Image = image.load(resources .. "Ch1.pic") +local Ch2Image = image.load(resources .. "Ch2.pic") + +local workspace = GUI.workspace() + +--------------------------------------------------------------------------------------------- + +local function loadContacts() + if filesystem.exists(pathToContacts) then + contacts = filesystem.readTable(pathToContacts) + end +end + +local function saveContacts() + filesystem.writeTable(pathToContacts, contacts) +end + +local function chevronDraw(object) + local inactiveColor, activeColor, fadeColor = 0x332400, 0xFFDB00, 0xCC6D00 + -- screen.drawRectangle(object.x, object.y, object.width, object.height, object.isActivated and fadeColor or inactiveColor) + -- screen.drawRectangle(object.x + 1, object.y, 3, object.height, object.isActivated and activeColor or inactiveColor) + -- screen.drawText(object.x + 2, object.y + 1, object.isActivated and 0x0 or 0xFFFFFF, object.text) + screen.drawImage(object.x, object.y, object.isActivated and Ch1Image or Ch2Image) + return object +end + +local function newChevronObject(x, y) + local object = GUI.object(x, y, 5, 3) + + object.draw = chevronDraw + object.isActivated = false + object.text = " " + + return object +end + +local function addChevron(x, y) + table.insert(workspace.chevrons, workspace.chevronsContainer:addChild(newChevronObject(x, y))) +end + +local function updateChevrons(state) + for i = 1, #workspace.chevrons do + workspace.chevrons[i].isActivated = state + if not state then workspace.chevrons[i].text = " " end + end +end + +local function updateButtons() + workspace.removeContactButton.disabled = #contacts == 0 + workspace.connectContactButton.disabled = #contacts == 0 +end + +local function update() + local stargateState, irisState, imagePath = stargate.stargateState(), stargate.irisState() + workspace.irisButton.text = irisState == "Closed" and "Open Iris" or "Close Iris" + workspace.connectionButton.text = stargateState == "Connected" and "Disconnect" or "Connect" + workspace.connectedToLabel.text = stargateState == "Connected" and "(Connected to " .. stargate.remoteAddress() .. ")" or "(Not connected)" + + if stargateState == "Connected" then + workspace.connectContactButton.disabled = true + workspace.messageContactButton.disabled = false + + if irisState == "Closed" then + imagePath = "OnOn.pic" + else + imagePath = "OnOff.pic" + end + else + workspace.connectContactButton.disabled = false + workspace.messageContactButton.disabled = true + + if irisState == "Closed" then + imagePath = "OffOn.pic" + else + imagePath = "OffOff.pic" + end + end + + updateButtons() + workspace.SGImage.image = image.load(resources .. imagePath) +end + +local function updateContacts() + workspace.contactsComboBox:clear() + if #contacts == 0 then + workspace.contactsComboBox:addItem("No contacts found") + else + for i = 1, #contacts do + workspace.contactsComboBox:addItem(contacts[i].name) + end + end +end + +local function newThing(x, y, width, height) + local object = GUI.object(x, y, width, height) + + object.draw = function(object) + local x, y = object.x + object.width - 1, math.floor(object.y + object.height / 2) + for i = object.y, object.y + object.height - 1 do + screen.drawText(x, i, 0xEEEEEE, "│") + end + for i = object.x, object.x + width - 1 do + screen.drawText(i, y, 0xEEEEEE, "─") + end + screen.drawText(x, y, 0xEEEEEE, "┤") + end + + return object +end + +local function dial(address) + local success, reason = stargate.dial(address) + if success then + workspace.fuelProgressBar.value = math.ceil(stargate.energyToDial(address) / stargate.energyAvailable() * 100) + workspace:draw() + else + GUI.alert("Failed to dial: " .. tostring(reason)) + end +end + +--------------------------------------------------------------------------------------------- + +local width, height = 32, 37 +local x, y = workspace.width - width - 3, math.floor(workspace.height / 2 - height / 2) + +workspace:addChild(GUI.panel(1, 1, workspace.width, workspace.height, 0x1E1E1E)) + +workspace.SGImage = workspace:addChild(GUI.image(1, 1, image.load(resources .. "OffOff.pic"))) +workspace.SGImage.localX, workspace.SGImage.localY = math.floor((x - 2) / 2 - image.getWidth(workspace.SGImage.image) / 2), workspace.height - image.getHeight(workspace.SGImage.image) + 1 + +workspace.chevronsContainer = workspace:addChild(GUI.container(workspace.SGImage.localX, workspace.SGImage.localY, workspace.SGImage.width, workspace.SGImage.height)) +workspace.chevrons = {} +addChevron(13, 30) +addChevron(8, 17) +addChevron(21, 6) +addChevron(45, 1) +addChevron(72, 6) +addChevron(83, 17) +addChevron(79, 30) + +workspace:addChild(newThing(workspace.SGImage.localX + workspace.SGImage.width, y, workspace.width - workspace.SGImage.localX - workspace.SGImage.width - width - 7, height)) + +workspace:addChild(GUI.label(x, y, width, 1, 0xEEEEEE, "Stargate " .. stargate.localAddress())):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); y = y + 1 +workspace.connectedToLabel = workspace:addChild(GUI.label(x, y, width, 1, 0x555555, "(Not connected)")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); y = y + 2 +workspace.connectionButton = workspace:addChild(GUI.framedButton(x, y, width, 3, 0xEEEEEE, 0xEEEEEE, 0xBBBBBB, 0xBBBBBB, "Connect")); y = y + 3 +-- workspace.connectionButton.animated = false +workspace.connectionButton.onTouch = function() + if stargate.stargateState() == "Idle" then + local container = GUI.addBackgroundContainer(workspace, true, true, "Connect") + local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xEEEEEE, 0x666666, 0x666666, 0xEEEEEE, 0x262626, contacts.last, "Type address here")) + input.onInputFinished = function() + if input.text then + dial(input.text) + contacts.last = input.text + saveContacts() + container:remove() + + workspace:draw() + end + end + + container.panel.eventHandler = function(workspace, object, e1) + if e1 == "touch" then + input.onInputFinished() + end + end + + workspace:draw() + else + stargate.disconnect() + end +end + +workspace.irisButton = workspace:addChild(GUI.framedButton(x, y, width, 3, 0xEEEEEE, 0xEEEEEE, 0xBBBBBB, 0xBBBBBB, "Open Iris")); y = y + 3 +workspace.irisButton.onTouch = function() + if stargate.irisState() == "Open" then + stargate.closeIris() + else + stargate.openIris() + end +end + +workspace.messageContactButton = workspace:addChild(GUI.framedButton(x, y, width, 3, 0xEEEEEE, 0xEEEEEE, 0xBBBBBB, 0xBBBBBB, "Message")); y = y + 4 +workspace.messageContactButton.onTouch = function() + local container = GUI.addBackgroundContainer(workspace, true, true, "Message") + local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xEEEEEE, 0x666666, 0x666666, 0xEEEEEE, 0x262626, nil, "Type message text here")) + input.onInputFinished = function() + if input.text then + container:remove() + stargate.sendMessage(input.text) + + workspace:draw() + end + end + + container.panel.eventHandler = function(workspace, object, e1) + if e1 == "touch" then + input.onInputFinished() + end + end + + workspace:draw() +end + +workspace:addChild(GUI.label(x, y, width, 1, 0xEEEEEE, "Contacts")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); y = y + 2 +workspace.contactsComboBox = workspace:addChild(GUI.comboBox(x, y, width, 3, 0x3C3C3C, 0xBBBBBB, 0x555555, 0x888888)); y = y + 4 + +workspace.connectContactButton = workspace:addChild(GUI.framedButton(x, y, width, 3, 0xEEEEEE, 0xEEEEEE, 0xBBBBBB, 0xBBBBBB, "Connect")); y = y + 3 +workspace.connectContactButton.onTouch = function() + dial(contacts[workspace.contactsComboBox.selectedItem].address) +end + +workspace.addContactButton = workspace:addChild(GUI.framedButton(x, y, width, 3, 0xEEEEEE, 0xEEEEEE, 0xBBBBBB, 0xBBBBBB, "Add contact")); y = y + 3 +workspace.addContactButton.onTouch = function() + local container = GUI.addBackgroundContainer(workspace, true, true, "Add contact") + local input1 = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xEEEEEE, 0x666666, 0x666666, 0xEEEEEE, 0x262626, nil, "Name")) + local input2 = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xEEEEEE, 0x666666, 0x666666, 0xEEEEEE, 0x262626, contacts.last, "Address")) + + container.panel.eventHandler = function(workspace, object, e1) + if e1 == "touch" then + if input1.text and input2.text then + local exists = false + for i = 1, #contacts do + if contacts[i].address == input2.text then + exists = true + break + end + end + if not exists then + table.insert(contacts, {name = input1.text, address = input2.text}) + updateContacts() + saveContacts() + updateButtons() + end + + container:remove() + workspace:draw() + end + end + end + + workspace:draw() +end + +workspace.removeContactButton = workspace:addChild(GUI.framedButton(x, y, width, 3, 0xEEEEEE, 0xEEEEEE, 0xBBBBBB, 0xBBBBBB, "Remove contact")); y = y + 4 +workspace.removeContactButton.onTouch = function() + if #contacts > 0 then + table.remove(contacts, workspace.contactsComboBox.selectedItem) + updateContacts() + saveContacts() + updateButtons() + + workspace:draw() + end +end + +workspace:addChild(GUI.label(x, y, width, 1, 0xEEEEEE, "Energy to dial")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); y = y + 2 +workspace.fuelProgressBar = workspace:addChild(GUI.progressBar(x, y, width, 0xBBBBBB, 0x0, 0xEEEEEE, 100, true, true, "", "%")); y = y + 3 +workspace.exitButton = workspace:addChild(GUI.framedButton(x, y, width, 3, 0xEEEEEE, 0xEEEEEE, 0xBBBBBB, 0xBBBBBB, "Exit")); y = y + 4 +workspace.exitButton.onTouch = function() + workspace:stop() +end + +workspace.eventHandler = function(workspace, object, e1, e2, e3, e4) + if e1 == "sgIrisStateChange" then + update() + workspace:draw() + elseif e1 == "sgStargateStateChange" then + if e3 == "Idle" or e3 == "Connected" then + update() + updateChevrons(e3 == "Connected") + workspace:draw() + end + elseif e1 == "sgChevronEngaged" then + if workspace.chevrons[e3] then + workspace.chevrons[e3].isActivated = true + workspace.chevrons[e3].text = e4 + workspace:draw() + end + elseif e1 == "sgMessageReceived" then + GUI.alert(e3) + end +end + +loadContacts() +updateContacts() +update() +updateChevrons(stargate.stargateState() == "Connected") + +workspace:draw() +workspace:start() \ No newline at end of file diff --git a/Applications/Stargate/OffOff.pic b/Applications/Stargate.app/OffOff.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/Stargate/OffOff.pic rename to Applications/Stargate.app/OffOff.pic diff --git a/Applications/Stargate/OffOn.pic b/Applications/Stargate.app/OffOn.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/Stargate/OffOn.pic rename to Applications/Stargate.app/OffOn.pic diff --git a/Applications/Stargate/OnOff.pic b/Applications/Stargate.app/OnOff.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/Stargate/OnOff.pic rename to Applications/Stargate.app/OnOff.pic diff --git a/Applications/Stargate/OnOn.pic b/Applications/Stargate.app/OnOn.pic old mode 100644 new mode 100755 similarity index 100% rename from Applications/Stargate/OnOn.pic rename to Applications/Stargate.app/OnOn.pic diff --git a/Applications/Stargate/Main.lua b/Applications/Stargate/Main.lua deleted file mode 100755 index ffff8c0b..00000000 --- a/Applications/Stargate/Main.lua +++ /dev/null @@ -1,312 +0,0 @@ - -local fs = require("filesystem") -local image = require("image") -local buffer = require("doubleBuffering") -local GUI = require("GUI") -local component = require("component") -local unicode = require("unicode") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") -local MineOSInterface = require("MineOSInterface") -if not component.isAvailable("stargate") then - GUI.alert("This program requires stargate from mod \"SGCraft\"") - return -end -local stargate = component.stargate - ---------------------------------------------------------------------------------------------- - -local resources = MineOSCore.getCurrentScriptDirectory() -local pathToContacts = MineOSPaths.applicationData .. "Stargate/Contacts.cfg" -local contacts = {} -local Ch1Image = image.load(resources .. "Ch1.pic") -local Ch2Image = image.load(resources .. "Ch2.pic") - -local application = GUI.application() - ---------------------------------------------------------------------------------------------- - -local function loadContacts() - if fs.exists(pathToContacts) then - contacts = table.fromFile(pathToContacts) - end -end - -local function saveContacts() - table.toFile(pathToContacts, contacts) -end - -local function chevronDraw(object) - local inactiveColor, activeColor, fadeColor = 0x332400, 0xFFDB00, 0xCC6D00 - -- buffer.drawRectangle(object.x, object.y, object.width, object.height, object.isActivated and fadeColor or inactiveColor) - -- buffer.drawRectangle(object.x + 1, object.y, 3, object.height, object.isActivated and activeColor or inactiveColor) - -- buffer.drawText(object.x + 2, object.y + 1, object.isActivated and 0x0 or 0xFFFFFF, object.text) - buffer.drawImage(object.x, object.y, object.isActivated and Ch1Image or Ch2Image) - return object -end - -local function newChevronObject(x, y) - local object = GUI.object(x, y, 5, 3) - - object.draw = chevronDraw - object.isActivated = false - object.text = " " - - return object -end - -local function addChevron(x, y) - table.insert(application.chevrons, application.chevronsContainer:addChild(newChevronObject(x, y))) -end - -local function updateChevrons(state) - for i = 1, #application.chevrons do - application.chevrons[i].isActivated = state - if not state then application.chevrons[i].text = " " end - end -end - -local function updateButtons() - application.removeContactButton.disabled = #contacts == 0 - application.connectContactButton.disabled = #contacts == 0 -end - -local function update() - local stargateState, irisState, imagePath = stargate.stargateState(), stargate.irisState() - application.irisButton.text = irisState == "Closed" and "Open Iris" or "Close Iris" - application.connectionButton.text = stargateState == "Connected" and "Disconnect" or "Connect" - application.connectedToLabel.text = stargateState == "Connected" and "(Connected to " .. stargate.remoteAddress() .. ")" or "(Not connected)" - - if stargateState == "Connected" then - application.connectContactButton.disabled = true - application.messageContactButton.disabled = false - - if irisState == "Closed" then - imagePath = "OnOn.pic" - else - imagePath = "OnOff.pic" - end - else - application.connectContactButton.disabled = false - application.messageContactButton.disabled = true - - if irisState == "Closed" then - imagePath = "OffOn.pic" - else - imagePath = "OffOff.pic" - end - end - - updateButtons() - application.SGImage.image = image.load(resources .. imagePath) -end - -local function updateContacts() - application.contactsComboBox:clear() - if #contacts == 0 then - application.contactsComboBox:addItem("No contacts found") - else - for i = 1, #contacts do - application.contactsComboBox:addItem(contacts[i].name) - end - end -end - -local function newThing(x, y, width, height) - local object = GUI.object(x, y, width, height) - - object.draw = function(object) - local x, y = object.x + object.width - 1, math.floor(object.y + object.height / 2) - for i = object.y, object.y + object.height - 1 do - buffer.drawText(x, i, 0xEEEEEE, "│") - end - for i = object.x, object.x + width - 1 do - buffer.drawText(i, y, 0xEEEEEE, "─") - end - buffer.drawText(x, y, 0xEEEEEE, "┤") - end - - return object -end - -local function dial(address) - local success, reason = stargate.dial(address) - if success then - application.fuelProgressBar.value = math.ceil(stargate.energyToDial(address) / stargate.energyAvailable() * 100) - application:draw() - else - GUI.alert("Failed to dial: " .. tostring(reason)) - end -end - ---------------------------------------------------------------------------------------------- - -local width, height = 32, 37 -local x, y = application.width - width - 3, math.floor(application.height / 2 - height / 2) - -application:addChild(GUI.panel(1, 1, application.width, application.height, 0x1E1E1E)) - -application.SGImage = application:addChild(GUI.image(1, 1, image.load(resources .. "OffOff.pic"))) -application.SGImage.localX, application.SGImage.localY = math.floor((x - 2) / 2 - image.getWidth(application.SGImage.image) / 2), application.height - image.getHeight(application.SGImage.image) + 1 - -application.chevronsContainer = application:addChild(GUI.container(application.SGImage.localX, application.SGImage.localY, application.SGImage.width, application.SGImage.height)) -application.chevrons = {} -addChevron(13, 30) -addChevron(8, 17) -addChevron(21, 6) -addChevron(45, 1) -addChevron(72, 6) -addChevron(83, 17) -addChevron(79, 30) - -application:addChild(newThing(application.SGImage.localX + application.SGImage.width, y, application.width - application.SGImage.localX - application.SGImage.width - width - 7, height)) - -application:addChild(GUI.label(x, y, width, 1, 0xEEEEEE, "Stargate " .. stargate.localAddress())):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); y = y + 1 -application.connectedToLabel = application:addChild(GUI.label(x, y, width, 1, 0x555555, "(Not connected)")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); y = y + 2 -application.connectionButton = application:addChild(GUI.framedButton(x, y, width, 3, 0xEEEEEE, 0xEEEEEE, 0xBBBBBB, 0xBBBBBB, "Connect")); y = y + 3 --- application.connectionButton.animated = false -application.connectionButton.onTouch = function() - if stargate.stargateState() == "Idle" then - local container = MineOSInterface.addBackgroundContainer(application, "Connect") - local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xEEEEEE, 0x666666, 0x666666, 0xEEEEEE, 0x262626, contacts.last, "Type address here")) - input.onInputFinished = function() - if input.text then - dial(input.text) - contacts.last = input.text - saveContacts() - container:remove() - - application:draw() - end - end - - container.panel.eventHandler = function(application, object, e1) - if e1 == "touch" then - input.onInputFinished() - end - end - - application:draw() - else - stargate.disconnect() - end -end - -application.irisButton = application:addChild(GUI.framedButton(x, y, width, 3, 0xEEEEEE, 0xEEEEEE, 0xBBBBBB, 0xBBBBBB, "Open Iris")); y = y + 3 -application.irisButton.onTouch = function() - if stargate.irisState() == "Open" then - stargate.closeIris() - else - stargate.openIris() - end -end - -application.messageContactButton = application:addChild(GUI.framedButton(x, y, width, 3, 0xEEEEEE, 0xEEEEEE, 0xBBBBBB, 0xBBBBBB, "Message")); y = y + 4 -application.messageContactButton.onTouch = function() - local container = MineOSInterface.addBackgroundContainer(application, "Message") - local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xEEEEEE, 0x666666, 0x666666, 0xEEEEEE, 0x262626, nil, "Type message text here")) - input.onInputFinished = function() - if input.text then - container:remove() - stargate.sendMessage(input.text) - - application:draw() - end - end - - container.panel.eventHandler = function(application, object, e1) - if e1 == "touch" then - input.onInputFinished() - end - end - - application:draw() -end - -application:addChild(GUI.label(x, y, width, 1, 0xEEEEEE, "Contacts")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); y = y + 2 -application.contactsComboBox = application:addChild(GUI.comboBox(x, y, width, 3, 0x3C3C3C, 0xBBBBBB, 0x555555, 0x888888)); y = y + 4 - -application.connectContactButton = application:addChild(GUI.framedButton(x, y, width, 3, 0xEEEEEE, 0xEEEEEE, 0xBBBBBB, 0xBBBBBB, "Connect")); y = y + 3 -application.connectContactButton.onTouch = function() - dial(contacts[application.contactsComboBox.selectedItem].address) -end - -application.addContactButton = application:addChild(GUI.framedButton(x, y, width, 3, 0xEEEEEE, 0xEEEEEE, 0xBBBBBB, 0xBBBBBB, "Add contact")); y = y + 3 -application.addContactButton.onTouch = function() - local container = MineOSInterface.addBackgroundContainer(application, "Add contact") - local input1 = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xEEEEEE, 0x666666, 0x666666, 0xEEEEEE, 0x262626, nil, "Name")) - local input2 = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xEEEEEE, 0x666666, 0x666666, 0xEEEEEE, 0x262626, contacts.last, "Address")) - - container.panel.eventHandler = function(application, object, e1) - if e1 == "touch" then - if input1.text and input2.text then - local exists = false - for i = 1, #contacts do - if contacts[i].address == input2.text then - exists = true - break - end - end - if not exists then - table.insert(contacts, {name = input1.text, address = input2.text}) - updateContacts() - saveContacts() - updateButtons() - end - - container:remove() - application:draw() - end - end - end - - application:draw() -end - -application.removeContactButton = application:addChild(GUI.framedButton(x, y, width, 3, 0xEEEEEE, 0xEEEEEE, 0xBBBBBB, 0xBBBBBB, "Remove contact")); y = y + 4 -application.removeContactButton.onTouch = function() - if #contacts > 0 then - table.remove(contacts, application.contactsComboBox.selectedItem) - updateContacts() - saveContacts() - updateButtons() - - application:draw() - end -end - -application:addChild(GUI.label(x, y, width, 1, 0xEEEEEE, "Energy to dial")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP); y = y + 2 -application.fuelProgressBar = application:addChild(GUI.progressBar(x, y, width, 0xBBBBBB, 0x0, 0xEEEEEE, 100, true, true, "", "%")); y = y + 3 -application.exitButton = application:addChild(GUI.framedButton(x, y, width, 3, 0xEEEEEE, 0xEEEEEE, 0xBBBBBB, 0xBBBBBB, "Exit")); y = y + 4 -application.exitButton.onTouch = function() - application:stop() -end - -application.eventHandler = function(application, object, e1, e2, e3, e4) - if e1 == "sgIrisStateChange" then - update() - application:draw() - elseif e1 == "sgStargateStateChange" then - if e3 == "Idle" or e3 == "Connected" then - update() - updateChevrons(e3 == "Connected") - application:draw() - end - elseif e1 == "sgChevronEngaged" then - if application.chevrons[e3] then - application.chevrons[e3].isActivated = true - application.chevrons[e3].text = e4 - application:draw() - end - elseif e1 == "sgMessageReceived" then - GUI.alert(e3) - end -end - -loadContacts() -updateContacts() -update() -updateChevrons(stargate.stargateState() == "Connected") - -application:draw() -application:start() \ No newline at end of file diff --git a/Applications/Symbols/Icon.pic b/Applications/Symbols.app/Icon.pic similarity index 100% rename from Applications/Symbols/Icon.pic rename to Applications/Symbols.app/Icon.pic diff --git a/Applications/Symbols/Main.lua b/Applications/Symbols.app/Main.lua similarity index 89% rename from Applications/Symbols/Main.lua rename to Applications/Symbols.app/Main.lua index c1d67a0b..1ffff757 100644 --- a/Applications/Symbols/Main.lua +++ b/Applications/Symbols.app/Main.lua @@ -1,9 +1,8 @@ local GUI = require("GUI") -local fs = require("filesystem") -local unicode = require("unicode") -local MineOSPaths = require("MineOSPaths") -local MineOSInterface = require("MineOSInterface") +local filesystem = require("Filesystem") +local paths = require("Paths") +local system = require("System") -------------------------------------------------------------------- @@ -14,14 +13,14 @@ local buttonHeight = 3 local horizontalSpacing = 2 local verticalSpacing = 1 -local recentCharsPath = MineOSPaths.applicationData .. "Symbols/Recent3.cfg" +local recentCharsPath = paths.user.applicationData .. "Symbols/Recent3.cfg" local recent = { 169, 170, 171, 172, 173 } -------------------------------------------------------------------- -local application, window = MineOSInterface.addWindow(GUI.filledWindow(1, 1, 90, 23, 0xE1E1E1)) +local workspace, window = system.addWindow(GUI.filledWindow(1, 1, 90, 23, 0xE1E1E1)) local sidePanel = window:addChild(GUI.panel(1, 1, 17, 1, 0x2D2D2D)) local buttonsContainer = window:addChild(GUI.container(3, 2, 1, 1)) @@ -64,13 +63,13 @@ recentLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL) recentLayout:setSpacing(1, 1, 0) recentLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) -local function onRecentButtonTouch(application, object) +local function onRecentButtonTouch(workspace, object) fromChar = object.code selectedChar = object.code updateChars() updateTexts() - application:draw() + workspace:draw() end local function updateRecent() @@ -86,7 +85,7 @@ for i = 1, #recent do step = not step end -local function charButtonOnTouch(application, button) +local function charButtonOnTouch(workspace, button) selectedChar = fromChar + button:indexOf() - 1 table.insert(recent, 1, selectedChar) table.remove(recent, #recent) @@ -95,8 +94,8 @@ local function charButtonOnTouch(application, button) updateTexts() updateRecent() - application:draw() - table.toFile(recentCharsPath, recent) + workspace:draw() + filesystem.writeTable(recentCharsPath, recent) end window.onResize = function(width, height) @@ -131,7 +130,7 @@ window.onResize = function(width, height) updateChars() updateTexts() - application:draw() + workspace:draw() end local function onAny() @@ -147,7 +146,7 @@ nextButton.onTouch = function() updateChars() updateTexts() - application:draw() + workspace:draw() end prevButton.onTouch = function() @@ -156,7 +155,7 @@ prevButton.onTouch = function() updateChars() updateTexts() - application:draw() + workspace:draw() end local overrideWindowEventHandler = window.eventHandler @@ -180,7 +179,7 @@ gotoInput.onInputFinished = function() updateChars() updateTexts() - application:draw() + workspace:draw() end end @@ -194,8 +193,8 @@ end -------------------------------------------------------------------- -if fs.exists(recentCharsPath) then - recent = table.fromFile(recentCharsPath) +if filesystem.exists(recentCharsPath) then + recent = filesystem.readTable(recentCharsPath) end updateRecent() diff --git a/Applications/Tetris/Icon.pic b/Applications/Tetris/Icon.pic deleted file mode 100644 index 3f8b8efcb2b2cdd3be4e8980e531e3a2f0785a17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 285 zcmeCoPtHt};^JUo0E7Pu|2-X;|2Hsz=rgzXGd!BsLXZVk57Q5$Vdel00WujBaM3Vz zFnur@CJv+_?r0#$`kxDQH%vc_2I~4h3F4ps{~Z_}&6$lZ(!UEN0+a*!8LSqj7XbEF Bc@h8s diff --git a/Applications/Tetris/Tetris.lua b/Applications/Tetris/Tetris.lua deleted file mode 100644 index 6eea9b98..00000000 --- a/Applications/Tetris/Tetris.lua +++ /dev/null @@ -1,266 +0,0 @@ - - - --------------------------------------------- Библиотеки ------------------------------------------------------------- - -local component = require("component") -local colorlib = require("colorlib") -local gpu = component.gpu - -local tetris = {} - --------------------------------------------- Переменные ------------------------------------------------------------- - -tetris.screen = { - main = { - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 1, 1, 1, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, - {0, 0, 1, 1, 1, 1, 1, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - }, - mini = { - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - }, - score = 538, - highScore = 1000, - speed = 1, - level = 1, -} - -local colors = { - tetrisColor = 0xFF5555, - screen = 0xCCDBBF, - pixel = 0x000000, - button = 0xFFFF00, -} - -local function calculateBrightness(color1, brightness) - local color - if brightness < 0 then color = 0x000000 else color = 0xFFFFFF end - brightness = math.abs(brightness) - return colorlib.alphaBlend(color1, color, brightness) -end - -local function recalculateColors() - --Всякие тени, света для корпуса - colors.light1 = calculateBrightness(colors.tetrisColor, 0xCC) - colors.light2 = calculateBrightness(colors.tetrisColor, 0xAA) - colors.light3 = calculateBrightness(colors.tetrisColor, 0x55) - colors.shadow1 = calculateBrightness(colors.tetrisColor, -(0xCC)) - colors.shadow2 = calculateBrightness(colors.tetrisColor, -(0xAA)) - colors.shadow3 = calculateBrightness(colors.tetrisColor, -(0x77)) - --Для кнопочек - if colors.button > 0x777777 then - colors.buttonText = calculateBrightness(colors.button, -(0x77)) - else - colors.buttonText = calculateBrightness(colors.button, 0x77) - end -end - -local sizes = {} -sizes.xScreenOffset = 4 -sizes.yScreenOffset = 3 - --------------------------------------------- Функции ------------------------------------------------------------- - -function tetris.recalculateSizes() - sizes.widthOfScreen = #tetris.screen.main[1] * 2 + (function() if tetris.showInfoPanel then return 10 else return 0 end end)() - sizes.heightOfScreen = #tetris.screen.main -end - -function tetris.generateScreenArray(width, height) - tetris.screen.main = {} - for j = 1, height do - tetris.screen.main[j] = {} - for i = 1, width do - tetris.screen.main[j][i] = 0 - end - end -end - -function tetris.drawOnlyMainScreen() - local xPos, yPos = tetris.xScreen, tetris.yScreen - for j = 1, #tetris.screen.main do - xPos = tetris.xScreen - for i = 1, #tetris.screen.main[j] do - if tetris.screen.main[j][i] == 1 then - gpu.set(xPos, yPos, "⬛") - else - gpu.set(xPos, yPos, "⬜") - end - - xPos = xPos + 2 - end - yPos = yPos + 1 - end - xPos, yPos = nil, nil -end - -function tetris.drawOnlyMiniScreen(x, y) - local xPos, yPos = x, y - for j = 1, #tetris.screen.mini do - xPos = x - for i = 1, #tetris.screen.mini[j] do - if tetris.screen.mini[j][i] == 1 then - gpu.set(xPos, yPos, "⬛") - else - gpu.set(xPos, yPos, "⬜") - end - - xPos = xPos + 2 - end - yPos = yPos + 1 - end - xPos, yPos = nil, nil -end - -function tetris.getPixel(x, y, whichScreen) - return tetris.screen[whichScreen or "main"][y][x] -end - -function tetris.setPixel(x, y, state, whichScreen) - tetris.screen[whichScreen or "main"][y][x] = state -end - -function tetris.changeColors(caseColor, buttonsColor, screenColor, pixelsColor) - colors.tetrisColor = caseColor or colors.tetrisColor - colors.button = buttonsColor or colors.button - colors.screen = screenColor or colors.screen - colors.pixel = pixelsColor or colors.pixel -end - -function tetris.drawScreen() - local xPos, yPos = tetris.xScreen, tetris.yScreen - --Рисуем квадрат экрана - ecs.square(xPos, yPos, sizes.widthOfScreen, sizes.heightOfScreen, colors.screen) - --Делаем цвет пикселей - gpu.setForeground(colors.pixel) - tetris.drawOnlyMainScreen() - - --Если показывать инфопанель = труе, то показать, хули - if tetris.showInfoPanel then - xPos, yPos = xPos + sizes.widthOfScreen - 9, yPos + 1 - - gpu.set(xPos + 1, yPos, "Score:"); yPos = yPos + 1 - gpu.set(xPos + 2, yPos, tostring(tetris.screen.score)); yPos = yPos + 2 - - gpu.set(xPos, yPos, "HiScore:"); yPos = yPos + 1 - gpu.set(xPos + 1, yPos, tostring(tetris.screen.highScore)); yPos = yPos + 2 - - gpu.set(xPos + 2, yPos, "Next:"); yPos = yPos + 1 - - tetris.drawOnlyMiniScreen(xPos, yPos); yPos = yPos + 5 - - gpu.set(xPos + 1, yPos, "Speed:"); yPos = yPos + 1 - gpu.set(xPos + 3, yPos, tostring(tetris.screen.speed)); yPos = yPos + 2 - - gpu.set(xPos + 1, yPos, "Level:"); yPos = yPos + 1 - gpu.set(xPos + 3, yPos, tostring(tetris.screen.level)); yPos = yPos + 2 - end -end - -function tetris.drawButtons() - local xPos, yPos = tetris.x + math.floor(sizes.caseWidth / 2 - 17), tetris.y + (sizes.heightOfScreen + sizes.yScreenOffset * 2 + 6) + 6 - - ecs.drawButton(xPos, yPos, 6, 3, "⮜", colors.button, colors.buttonText) - xPos = xPos + 12 - ecs.drawButton(xPos, yPos, 6, 3, "⮞", colors.button, colors.buttonText) - xPos = xPos - 6 - yPos = yPos - 3 - ecs.drawButton(xPos, yPos, 6, 3, "⮝", colors.button, colors.buttonText) - yPos = yPos + 3 * 2 - ecs.drawButton(xPos, yPos, 6, 3, "⮟", colors.button, colors.buttonText) - - xPos = xPos + 17 - yPos = yPos - 4 - ecs.square(xPos + 2, yPos, 6, 5, colors.button) - ecs.square(xPos, yPos + 1, 10, 3, colors.button) -end - -function tetris.drawCase() - --Делаем перерасчет размеров экрана - tetris.recalculateSizes() - --Рассчитываем размер корпуса - sizes.xSize, sizes.ySize = gpu.getResolution() - sizes.caseWidth = sizes.widthOfScreen + sizes.xScreenOffset * 2 - sizes.heightOfBottomThing = sizes.ySize - (sizes.heightOfScreen + sizes.yScreenOffset * 2 + 6) - tetris.y + 1 - local yPos = tetris.y - --Рисуем верхнюю штучку - ecs.square(tetris.x + 1, yPos, sizes.caseWidth - 2, 1, colors.light2) - yPos = yPos + 1 - --Рисуем всю штучку под экраном - ecs.square(tetris.x, yPos, sizes.caseWidth, sizes.heightOfScreen + sizes.yScreenOffset * 2 - 1, colors.tetrisColor) - ecs.square(tetris.x + sizes.xScreenOffset - 1, yPos + 1, sizes.widthOfScreen + 2, sizes.heightOfScreen + 2, colors.shadow1) - yPos = yPos + sizes.heightOfScreen + sizes.yScreenOffset * 2 - 1 - --Рисуем кольцевую штучку - ecs.square(tetris.x, yPos, sizes.caseWidth, 1, colors.light2); yPos = yPos + 1 - ecs.square(tetris.x, yPos, sizes.caseWidth, 1, colors.light1); yPos = yPos + 1 - ecs.square(tetris.x, yPos, sizes.caseWidth, 2, colors.tetrisColor); yPos = yPos + 2 - ecs.square(tetris.x, yPos, sizes.caseWidth, 1, colors.shadow1); yPos = yPos + 1 - ecs.square(tetris.x, yPos, sizes.caseWidth, 1, colors.shadow2); yPos = yPos + 1 - --Рисуем под кнопочками - ecs.square(tetris.x, yPos, sizes.caseWidth, sizes.heightOfBottomThing, colors.tetrisColor) -end - -function tetris.draw(x, y, screenResolutionWidth, screenResolutionHeight, showInfoPanel) - --Задаем переменную показа инфопанели - tetris.showInfoPanel = showInfoPanel - --Просчитываем цвета - recalculateColors() - --Создаем массив основного экрана нужной ширины и высоты - tetris.generateScreenArray(screenResolutionWidth, screenResolutionHeight) - --Рисуем корпус устройства - tetris.x, tetris.y = x, y - tetris.drawCase() - --Рисуем экран тетриса - tetris.xScreen, tetris.yScreen = tetris.x + sizes.xScreenOffset, tetris.y + sizes.yScreenOffset - tetris.drawScreen() - --Кнопочки рисуем - tetris.drawButtons() -end - --------------------------------------------- Программа ------------------------------------------------------------- - ---ecs.prepareToExit() - -tetris.changeColors(0x008800, 0xFFFF00) -tetris.draw(10, 5, 27, 20, false) -tetris.changeColors(0xFF5555, 0xFFFF00) -tetris.draw(80, 5, 10, 20, true) - -ecs.waitForTouchOrClick() - - - - - - - - - - - - - - - - diff --git a/Applications/Translate.app/Config.cfg b/Applications/Translate.app/Config.cfg new file mode 100644 index 00000000..149f9d52 --- /dev/null +++ b/Applications/Translate.app/Config.cfg @@ -0,0 +1 @@ +{["toLanguage"]="Казахский",["fromLanguage"]="Русский",["languages"]={[30]={[1]="is",[2]="Исландский"},[1]={[1]="az",[2]="Азербайджанский"},[7]={[1]="af",[2]="Африкаанс"},[31]={[1]="es",[2]="Испанский"},[32]={[1]="it",[2]="Итальянский"},[33]={[1]="kk",[2]="Казахский"},[34]={[1]="kn",[2]="Каннада"},[8]={[1]="eu",[2]="Баскский"},[35]={[1]="ca",[2]="Каталанский"},[36]={[1]="ky",[2]="Киргизский"},[37]={[1]="zh",[2]="Китайский"},[38]={[1]="ko",[2]="Корейский"},[9]={[1]="ba",[2]="Башкирский"},[39]={[1]="xh",[2]="Коса"},[40]={[1]="km",[2]="Кхмерский"},[41]={[1]="lo",[2]="Лаосский"},[42]={[1]="la",[2]="Латынь"},[10]={[1]="be",[2]="Белорусский"},[43]={[1]="lv",[2]="Латышский"},[44]={[1]="lt",[2]="Литовский"},[45]={[1]="lb",[2]="Люксембургский"},[2]={[1]="sq",[2]="Албанский"},[11]={[1]="bn",[2]="Бенгальский"},[47]={[1]="mg",[2]="Малагасийский"},[48]={[1]="ms",[2]="Малайский"},[49]={[1]="ml",[2]="Малаялам"},[50]={[1]="mt",[2]="Мальтийский"},[12]={[1]="my",[2]="Бирманский"},[51]={[1]="mi",[2]="Маори"},[52]={[1]="mr",[2]="Маратхи"},[53]={[1]="mhr",[2]="Марийский"},[54]={[1]="mn",[2]="Монгольский"},[13]={[1]="bg",[2]="Болгарский"},[55]={[1]="de",[2]="Немецкий"},[56]={[1]="ne",[2]="Непальский"},[57]={[1]="no",[2]="Норвежский"},[58]={[1]="pa",[2]="Панджаби"},[14]={[1]="bs",[2]="Боснийский"},[59]={[1]="pap",[2]="Папьяменто"},[60]={[1]="fa",[2]="Персидский"},[61]={[1]="pl",[2]="Польский"},[3]={[1]="am",[2]="Амхарский"},[15]={[1]="cy",[2]="Валлийский"},[63]={[1]="ro",[2]="Румынский"},[64]={[1]="ru",[2]="Русский"},[65]={[1]="ceb",[2]="Себуанский"},[16]={[1]="hu",[2]="Венгерский"},[67]={[1]="si",[2]="Сингальский"},[68]={[1]="sk",[2]="Словацкий"},[69]={[1]="sl",[2]="Словенский"},[17]={[1]="vi",[2]="Вьетнамский"},[71]={[1]="su",[2]="Сунданский"},[72]={[1]="tl",[2]="Тагальский"},[73]={[1]="tg",[2]="Таджикский"},[18]={[1]="ht",[2]="Гаитянский"},[75]={[1]="ta",[2]="Тамильский"},[76]={[1]="tt",[2]="Татарский"},[4]={[1]="en",[2]="Английский"},[19]={[1]="gl",[2]="Галисийский"},[79]={[1]="udm",[2]="Удмуртский"},[80]={[1]="uz",[2]="Узбекский"},[81]={[1]="uk",[2]="Украинский"},[20]={[1]="nl",[2]="Голландский"},[83]={[1]="fi",[2]="Финский"},[84]={[1]="fr",[2]="Французский"},[85]={[1]="hi",[2]="Хинди"},[21]={[1]="mrj",[2]="Горномарийский"},[87]={[1]="cs",[2]="Чешский"},[88]={[1]="sv",[2]="Шведский"},[89]={[1]="gd",[2]="Шотландский (гэльский)"},[22]={[1]="el",[2]="Греческий"},[91]={[1]="eo",[2]="Эсперанто"},[92]={[1]="et",[2]="Эстонский"},[5]={[1]="ar",[2]="Арабский"},[23]={[1]="ka",[2]="Грузинский"},[24]={[1]="gu",[2]="Гуджарати"},[25]={[1]="da",[2]="Датский"},[94]={[1]="ja",[2]="Японский"},[93]={[1]="jv",[2]="Яванский"},[26]={[1]="he",[2]="Иврит"},[90]={[1]="emj",[2]="Эмодзи"},[86]={[1]="hr",[2]="Хорватский"},[6]={[1]="hy",[2]="Армянский"},[27]={[1]="yi",[2]="Идиш"},[82]={[1]="ur",[2]="Урду"},[78]={[1]="tr",[2]="Турецкий"},[77]={[1]="te",[2]="Телугу"},[28]={[1]="id",[2]="Индонезийский"},[74]={[1]="th",[2]="Тайский"},[70]={[1]="sw",[2]="Суахили"},[66]={[1]="sr",[2]="Сербский"},[29]={[1]="ga",[2]="Ирландский"},[62]={[1]="pt",[2]="Португальский"},[46]={[1]="mk",[2]="Македонский"}},["APIKey"]="trnsl.1.1.20170831T153247Z.6ecf9d7198504994.8ce5a3aa9f9a2ecbe7b2377af37ffe5ad379f4ca"} \ No newline at end of file diff --git a/Applications/Translate/Icon.pic b/Applications/Translate.app/Icon.pic similarity index 100% rename from Applications/Translate/Icon.pic rename to Applications/Translate.app/Icon.pic diff --git a/Applications/Translate/Logo.pic b/Applications/Translate.app/Logo.pic similarity index 100% rename from Applications/Translate/Logo.pic rename to Applications/Translate.app/Logo.pic diff --git a/Applications/Translate/Main.lua b/Applications/Translate.app/Main.lua similarity index 82% rename from Applications/Translate/Main.lua rename to Applications/Translate.app/Main.lua index a15a7ec8..e886ae5d 100644 --- a/Applications/Translate/Main.lua +++ b/Applications/Translate.app/Main.lua @@ -1,17 +1,17 @@ -require("advancedLua") -local fs = require("filesystem") -local json = require("json") -local web = require("web") +local filesystem = require("Filesystem") +local json = require("JSON") +local internet = require("Internet") local GUI = require("GUI") -local buffer = require("doubleBuffering") -local image = require("image") -local unicode = require("unicode") +local screen = require("Screen") +local image = require("Image") +local paths = require("Paths") +local system = require("System") ------------------------------------------------------------------------------------------------------------------ -local resourcesPath = fs.path(getCurrentScript()) -local configPath = resourcesPath .. "Config.cfg" +local resourcesPath = filesystem.path(system.getCurrentScript()) +local configPath = paths.user.applicationData .. "Translate/Config.cfg" local config = { APIKey = "trnsl.1.1.20170831T153247Z.6ecf9d7198504994.8ce5a3aa9f9a2ecbe7b2377af37ffe5ad379f4ca", fromLanguage = "Русский", @@ -20,26 +20,26 @@ local config = { } local function saveConfig() - table.toFile(configPath, config) + filesystem.writeTable(configPath, config) end -if fs.exists(configPath) then - config = table.fromFile(configPath) +if filesystem.exists(configPath) then + config = filesystem.readTable(configPath) end ------------------------------------------------------------------------------------------------------------------ -local application = GUI.application() -application:addChild(GUI.panel(1, 1, application.width, application.height, 0x1E1E1E)) -local actionButtons = application:addChild(GUI.actionButtons(3, 2, true)) -local layout = application:addChild(GUI.layout(1, 1, application.width, application.height, 1, 1)) +local workspace = GUI.workspace() +workspace:addChild(GUI.panel(1, 1, workspace.width, workspace.height, 0x1E1E1E)) +local actionButtons = workspace:addChild(GUI.actionButtons(3, 2, true)) +local layout = workspace:addChild(GUI.layout(1, 1, workspace.width, workspace.height, 1, 1)) local logo = layout:addChild(GUI.image(1, 1, image.load(resourcesPath .. "Logo.pic"))) local elementWidth = image.getWidth(logo.image) layout:addChild(GUI.object(1, 1, 1, 1)) local fromLanguageContainer = layout:addChild(GUI.container(1, 1, elementWidth, 1)) -local fromLanguageAutoDetectButton = fromLanguageContainer:addChild(GUI.adaptiveButton(1, 1, 2, 0, 0x2D2D2D, 0xBBBBBB, 0x666666, 0xBBBBBB, "Определить язык")) +local fromLanguageAutoDetectButton = fromLanguageContainer:addChild(GUI.adaptiveButton(1, 1, 2, 0, 0x2D2D2D, 0xBBBBBB, 0x666666, 0xBBBBBB, "Detect language")) fromLanguageAutoDetectButton.localX = fromLanguageContainer.width - fromLanguageAutoDetectButton.width + 1 local fromComboBox = fromLanguageContainer:addChild(GUI.comboBox(1, 1, fromLanguageAutoDetectButton.localX - 3, 1, 0x2D2D2D, 0xAAAAAA, 0x444444, 0x888888)) local fromInputField = layout:addChild(GUI.input(1, 1, elementWidth, 5, 0x2D2D2D, 0x666666, 0x444444, 0x3C3C3C, 0xBBBBBB, nil, "Введите текст", true)) @@ -58,7 +58,7 @@ local infoLabel = layout:addChild(GUI.label(1, 1, elementWidth, 1, 0xFF6D40, " " local function status(text) infoLabel.text = text - application:draw() + workspace:draw() end local function getLanguageIndex(text, short) @@ -79,7 +79,7 @@ end local function checkLanguages() if #config.languages == 0 then - local result, reason = web.request("https://translate.yandex.net/api/v1.5/tr.json/getLangs?key=" .. config.APIKey .. "&ui=ru") + local result, reason = internet.request("https://translate.yandex.net/api/v1.5/tr.json/getLangs?key=" .. config.APIKey .. "&ui=ru") if result then fromComboBox:clear() toComboBox:clear() @@ -103,9 +103,9 @@ local function translate() if unicode.len(fromInputField.text or "") > 0 then status("Отправка запроса на перевод...") - local result, reason = web.request( + local result, reason = internet.request( "https://translate.yandex.net/api/v1.5/tr.json/translate?key=" .. config.APIKey .. - "&text=" .. string.optimizeForURLRequests(fromInputField.text) .. + "&text=" .. internet.encode(fromInputField.text) .. "&lang=" .. config.languages[getLanguageIndex(fromComboBox:getItem(fromComboBox.selectedItem).text, false)][1] .. "-" .. config.languages[getLanguageIndex(toComboBox:getItem(toComboBox.selectedItem).text, false)][1] ) @@ -127,9 +127,9 @@ fromLanguageAutoDetectButton.onTouch = function() if unicode.len(fromInputField.text or "") > 0 then status("Отправка запроса на определение языка...") - local result, reason = web.request( + local result, reason = internet.request( "https://translate.yandex.net/api/v1.5/tr.json/detect?key=" .. config.APIKey .. - "&text=" .. string.optimizeForURLRequests(fromInputField.text) + "&text=" .. internet.encode(fromInputField.text) ) if result then @@ -147,7 +147,7 @@ fromLanguageAutoDetectButton.onTouch = function() end actionButtons.close.onTouch = function() - application:stop() + workspace:stop() end switchButton.onTouch = function() @@ -184,5 +184,5 @@ checkLanguages() fromComboBox.selectedItem = getLanguageIndex(config.fromLanguage, false) toComboBox.selectedItem = getLanguageIndex(config.toLanguage, false) -application:draw() -application:start() +workspace:draw() +workspace:start() diff --git a/Applications/TurretControl/About/English.txt b/Applications/TurretControl/About/English.txt deleted file mode 100644 index 9afc9140..00000000 --- a/Applications/TurretControl/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Приложение, предназначенное для управления турелями из мода OpenModularTurrets. \ No newline at end of file diff --git a/Applications/TurretControl/About/Russian.txt b/Applications/TurretControl/About/Russian.txt deleted file mode 100644 index 9afc9140..00000000 --- a/Applications/TurretControl/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Приложение, предназначенное для управления турелями из мода OpenModularTurrets. \ No newline at end of file diff --git a/Applications/TurretControl/Icon.pic b/Applications/TurretControl/Icon.pic deleted file mode 100644 index 9effbea446d1127e7f9d3c8833ba0d72cbfa90f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 152 zcmW-ZxeWp_6h#00!~SJ#yrK&vB%%#CL^((-IgwkSFE;{XNAu>;Y{&Ddr0Y+7fj|@q zHIGu$os_+OnBSqQ*4s{A;p(-w7x~5sJVe+%ZFD?XSs3bfIu7?3XvjA%6kO*;l*h2V OJxujjn2Rckq5J_otrF${ diff --git a/Applications/TurretControl/Turret.pic b/Applications/TurretControl/Turret.pic deleted file mode 100644 index 46b99f7f80f8ed346eae0496111224667507093d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 324 zcmY*VL2iRU4D{IET_^#T5MYnFg7`(HN;&n?2US(xkO$@?sTWcyJ&dKXXFcQne7}77 z6!<}Mzg 0 then - local lines = string.wrap(data.text, width - localX) + local lines = text.wrap(data.text, width - localX) object.textBox = object:addChild(GUI.textBox(localX, localY, width - localX, #lines, nil, textColor, lines, 1, 0, 0)) object.textBox.eventHandler = nil localY = localY + object.textBox.height + 1 @@ -643,11 +642,11 @@ local function newPost(x, y, width, avatarWidth, senderColor, textColor, dateCol end local function uploadDocument(path) - local fileName = fs.name(path) + local fileName = filesystem.name(path) local uploadServer = methodRequest("docs.getUploadServer?") if uploadServer then local boundary = "----WebKitFormBoundaryISgaMqjePLcGZFOx" - local handle = io.open(path, "rb") + local handle = filesystem.open(path, "rb") local data = "--" .. boundary .. "\r\n" .. @@ -667,7 +666,7 @@ local function uploadDocument(path) } ) - table.toFile("/test.txt", { + filesystem.writeTable("/test.txt", { url = uploadServer.upload_url, data = data, size = #data, @@ -778,14 +777,14 @@ local function showHistory(container, peerID) attachedPath = nil end - local sendResult = methodRequest("messages.send?peer_id=" .. peerID .. (attachment or "") .. (#input.text > 0 and ("&message=" .. web.encode(input.text)) or "")) + local sendResult = methodRequest("messages.send?peer_id=" .. peerID .. (attachment or "") .. (#input.text > 0 and ("&message=" .. internet.encode(input.text)) or "")) if sendResult then showHistory(container, peerID) end end end - application:draw() + workspace:draw() end end @@ -934,7 +933,7 @@ showUserProfile = function(peerID) local eblo = list[index] layout:addChild(newAvatar(1, 1, 4, 2, getNameShortcut(eblo.first_name .. " " .. eblo.last_name), eblo.id)) - layout:addChild(GUI.text(1, 4, 0xA5A5A5, string.limit(eblo.first_name, friendsNameLimit))) + layout:addChild(GUI.text(1, 4, 0xA5A5A5, text.limit(eblo.first_name, friendsNameLimit))) index = index + 1 else @@ -985,7 +984,7 @@ showUserProfile = function(peerID) container:addChild(GUI.text(1, 1, style.blockKey, key .. ": ")) local width = container.width - maxProfileKeyLength - local lines = string.wrap(value, width) + local lines = text.wrap(value, width) container:addChild(GUI.textBox(maxProfileKeyLength, 1, width, #lines, nil, style.blockValue, lines, 1)).eventHandler = nil container.height = #lines @@ -1118,8 +1117,8 @@ showUserProfile = function(peerID) local function counterDraw(object) local centerX = object.x + object.width / 2 - buffer.drawText(math.floor(centerX - object.countLength / 2), object.y, style.blockTitle, object.count) - buffer.drawText(math.floor(centerX - object.descriptionLength / 2), object.y + 1, style.blockValue, object.description) + screen.drawText(math.floor(centerX - object.countLength / 2), object.y, style.blockTitle, object.count) + screen.drawText(math.floor(centerX - object.descriptionLength / 2), object.y + 1, style.blockValue, object.description) end for i = 1, #localization.profileCounters do @@ -1197,7 +1196,7 @@ showUserProfile = function(peerID) update() end - contentContainer.eventHandler = function(application, contentContainer, e1, e2, e3, e4, e5) + contentContainer.eventHandler = function(workspace, contentContainer, e1, e2, e3, e4, e5) if e1 == "scroll" then userContainer.localY = userContainer.localY + (e5 > 0 and 1 or -1) * config.scrollSpeed @@ -1209,7 +1208,7 @@ showUserProfile = function(peerID) userContainer.localY = 1 end - application:draw() + workspace:draw() end end end @@ -1319,7 +1318,7 @@ local function showFriends(peerID) local friends = getFriends() if friends then addFromList(friends) - application:draw() + workspace:draw() end end) end @@ -1387,7 +1386,7 @@ newsSelectable.onTouch = function() end end) - application:draw() + workspace:draw() end end @@ -1467,8 +1466,8 @@ showConversations = function(peerID) local senderName = getSenderName(list.profiles, item.conversation, list.groups, item.conversation.peer.id) local avatar = container:addChild(newAvatar(2, 1, 4, 2, getNameShortcut(senderName), item.conversation.peer.id)) - container.senderText = container:addChild(GUI.text(avatar.localX + avatar.width + 1, 1, 0x0, string.limit(senderName, container.width - avatar.width - 9))) - container.messagePreviewText = container:addChild(GUI.text(container.senderText.localX, 2, 0x0, string.limit(messagePreview, container.width - avatar.width - 3))) + container.senderText = container:addChild(GUI.text(avatar.localX + avatar.width + 1, 1, 0x0, text.limit(senderName, container.width - avatar.width - 9))) + container.messagePreviewText = container:addChild(GUI.text(container.senderText.localX, 2, 0x0, text.limit(messagePreview, container.width - avatar.width - 3))) container.dateText = container:addChild(GUI.text(container.width - 5, 1, 0x0, os.date("%H:%M", item.last_message.date))) container.draw = conversationDraw @@ -1494,7 +1493,7 @@ showConversations = function(peerID) if #layout.children > 0 then layout.children[1]:select() else - application:draw() + workspace:draw() end end end @@ -1565,7 +1564,7 @@ local function showDocuments() end) end - application:draw() + workspace:draw() end addPizda(localization.documents).onTouch = function() @@ -1597,7 +1596,7 @@ settingsSelectable.onTouch = function() local slider = layout:addChild(GUI.slider(1, 1, 36, style.UISliderPrimary, style.UISliderSecondary, style.UISliderPipe, style.UISliderValue, min, max, config[field], false, localization[field] .. ": ", "")) slider.roundValues = true slider.onValueChanged = function() - config[field] = math.round(slider.value) + config[field] = number.round(slider.value) saveConfig() end end @@ -1605,18 +1604,22 @@ settingsSelectable.onTouch = function() layout:addChild(GUI.text(1, 1, style.UITitle, localization.settingsStyle)) local comboBox = layout:addChild(GUI.comboBox(1, 1, 36, 3, style.UIComboBoxBackground, style.UIComboBoxForeground, style.UIComboBoxArrowBackground, style.UIComboBoxArrowForeground)) - for file in fs.list(stylesPath) do - comboBox:addItem(fs.hideExtension(file)).onTouch = function() - config.style = file - loadStyle() - applyStyle() - lastPizda() + + local list = filesystem.list(stylesPath) + for i = 1, #list do + if filesystem.extension(list[i]) == ".lua" then + comboBox:addItem(filesystem.hideExtension(list[i])).onTouch = function() + config.style = list[i] + loadStyle() + applyStyle() + lastPizda() - saveConfig() - end + saveConfig() + end - if file == config.style then - comboBox.selectedItem = comboBox:count() + if list[i] == config.style then + comboBox.selectedItem = comboBox:count() + end end end @@ -1630,7 +1633,7 @@ settingsSelectable.onTouch = function() addYobaSlider(2, 50, "loadCountDocs") addYobaSlider(2, 10, "scrollSpeed") - application:draw() + workspace:draw() end local function login() @@ -1653,7 +1656,7 @@ local function login() usernameInput.onInputFinished = function() loginButton.disabled = #usernameInput.text == 0 or #passwordInput.text == 0 - application:draw() + workspace:draw() end passwordInput.onInputFinished = usernameInput.onInputFinished @@ -1680,7 +1683,7 @@ local function login() lastPizda = login usernameInput.onInputFinished() - application:draw() + workspace:draw() end local function logout() diff --git a/Applications/VK/Styles/Bright.lua b/Applications/VK.app/Styles/Bright.lua similarity index 100% rename from Applications/VK/Styles/Bright.lua rename to Applications/VK.app/Styles/Bright.lua diff --git a/Applications/VK/Styles/Dark.lua b/Applications/VK.app/Styles/Dark.lua similarity index 100% rename from Applications/VK/Styles/Dark.lua rename to Applications/VK.app/Styles/Dark.lua diff --git a/Applications/VK/Styles/Default.lua b/Applications/VK.app/Styles/Default.lua similarity index 100% rename from Applications/VK/Styles/Default.lua rename to Applications/VK.app/Styles/Default.lua diff --git a/Applications/Viewer/About/English.txt b/Applications/Viewer/About/English.txt deleted file mode 100644 index 72244292..00000000 --- a/Applications/Viewer/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Программа для просмотра изображений в нашем формате .pic, имеется возможность проигрывания картинок в режиме слайдшоу. \ No newline at end of file diff --git a/Applications/Viewer/About/Russian.txt b/Applications/Viewer/About/Russian.txt deleted file mode 100644 index 72244292..00000000 --- a/Applications/Viewer/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Программа для просмотра изображений в нашем формате .pic, имеется возможность проигрывания картинок в режиме слайдшоу. \ No newline at end of file diff --git a/Applications/Viewer/Icon.pic b/Applications/Viewer/Icon.pic deleted file mode 100644 index e61626d780a656e99dedb2346d2d8a7f73fb1465..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 245 zcmYMsu?@m76a-M;`6u=mStUfWL`C5$+fXn7RU|4pD%L@wPR$5Na3&6^p1$ti9fp0f zXg$2kn5Sb3Zs+hE0m`>Dd6q#s3Hg#<4ziUu*<^J8Hfx);O|@CtaU>C!3DybL32I`V rP6-d?Zj2uE*0dq^uuq9D3HM(FTVI#kjJ^$--P(m=`;Dc0-ER8=coHkv diff --git a/Applications/Viewer/Viewer.lua b/Applications/Viewer/Viewer.lua deleted file mode 100755 index f08d36b4..00000000 --- a/Applications/Viewer/Viewer.lua +++ /dev/null @@ -1,218 +0,0 @@ - -local computer = require("computer") -local ecs = require("ECSAPI") -local image = require("image") -local fs = require("filesystem") -local buffer = require("doubleBuffering") -local unicode = require("unicode") -local event = require("event") - -local pathToApplicationResources = "MineOS/Applications/Viewer.app/Resources/" -local currentPath = "MineOS/Pictures/" -local imageList = {} -local currentImage = 1 -local showGUI = true -local slideShowInterval = 5 -local enableSlideShow = true - -local arrowLeftImage = image.load(pathToApplicationResources .. "arrowLeft.pic") -local arrowRightImage = image.load(pathToApplicationResources .. "arrowRight.pic") -local playImage = image.load(pathToApplicationResources .. "play.pic") -local wallpaperImage = image.load("MineOS/System/OS/Icons/Computer.pic") - -local obj = {} - -buffer.start() - -local function loadImageList() - local fileList = fs.list(currentPath) - imageList = {} - for file in fileList do - if ecs.getFileFormat(file) == ".pic" then - table.insert(imageList, currentPath .. file) - end - end -end - -local function drawImage() - if #imageList > 0 then - local xImage, yImage = 1, 1 - local currentLoadedImage = image.load(imageList[currentImage]) - - if currentLoadedImage.width < buffer.screen.width then xImage = math.floor(buffer.screen.width / 2 - currentLoadedImage.width / 2) end - if currentLoadedImage.height < buffer.screen.height then yImage = math.floor(buffer.screen.height / 2 - currentLoadedImage.height / 2) end - - buffer.image(xImage, yImage, currentLoadedImage) - currentLoadedImage = nil - else - local text = "Изображения в директории \"" .. currentPath .. "\" не найдены" - buffer.text(math.floor(buffer.screen.width / 2 - unicode.len(text) / 2), math.floor(buffer.screen.height / 2), 0x000000, text) - end -end - -local function multipleButtons(x, y, widthOfButton, heightOfButton, spaceBetweenButtons, ...) - local buttons = {...} - local objectsToReturn = {} - for i = 1, #buttons do - buffer.button(x, y, widthOfButton, heightOfButton, buttons[i][1], buttons[i][2], buttons[i][3]) - table.insert(objectsToReturn, {x, y, x + widthOfButton - 1, y + heightOfButton - 1}) - x = x + widthOfButton + spaceBetweenButtons - end - return objectsToReturn -end - -local function drawBottomButtons() - local y = buffer.screen.height - 4 - local x = math.floor(buffer.screen.width / 2 - 21) - - obj.arrowLeft = {x, y, x + 7, y + 3} - buffer.image(x, y, arrowLeftImage); x = x + 10 - obj.play = {x, y, x + 7, y + 3} - buffer.image(x, y, playImage); x = x + 10 - obj.arrowRight = {x, y, x + 7, y + 3} - buffer.image(x, y, arrowRightImage); x = x + 12 - obj.wallpaper = {x, y, x + 7, y + 3} - buffer.image(x, y, wallpaperImage) -end - -local function drawGUI() - if showGUI then - --Верхний бар - buffer.square(1, 1, buffer.screen.width, 1, 0xDDDDDD, 0xFFFFFF, " ") - local text = #imageList > 0 and ecs.stringLimit("start", imageList[currentImage], 40) or "Viewer" - buffer.text(math.floor(buffer.screen.width / 2 - unicode.len(text) / 2), 1, 0x000000, text) - buffer.text(2, 1, ecs.colors.red, "⬤") - buffer.text(5, 1, ecs.colors.orange, "⬤") - buffer.text(8, 1, ecs.colors.green, "⬤") - - --Нижний бар - local height = 6 - local transparency = 40 - local y = buffer.screen.height - height + 1 - buffer.square(1, y, buffer.screen.width, height, 0x000000, 0xFFFFFF, " ", transparency) - -- multipleButtons(math.floor(buffer.screen.width / 2 - 16), y, 7, 3, 2, {0xEEEEEE, 0x262626, "←"}, {0xEEEEEE, 0x262626, "►"}, {0xEEEEEE, 0x262626, "→"}, {0xEEEEEE, 0x262626, "♥"}) - drawBottomButtons() - end -end - -local function drawAll(force) - buffer.clear(0xFFFFFF) - - drawImage() - drawGUI() - - buffer.draw(force) -end - -local function prevImage() - currentImage = currentImage - 1 - if currentImage < 1 then currentImage = #imageList end - drawAll() -end - -local function nextImage() - currentImage = currentImage + 1 - if currentImage > #imageList then currentImage = 1 end - drawAll() -end - -local function slideShowDro4er() - nextImage() -end - -local function enableSlideShowDro4er() - enableSlideShow = true - _G.imageViewerSlideShowTimer = event.timer(slideShowInterval, slideShowDro4er, math.huge) -end - -local function clicked(x, y, obj) - if obj and ecs.clickedAtArea(x, y, obj[1], obj[2], obj[3], obj[4]) and #imageList > 0 then - return true - end - return false -end - -local function press(x, y) - buffer.square(x, y, 10, 6, 0x000000, 0xFFFFFF, " ", 60) - drawBottomButtons() - buffer.draw() - os.sleep(0.2) - drawAll() -end - ------------------------------------------------------------------------------------------------------------------------------------------------- - -local args = {...} - -if args[1] == "open" then - if args[2] then - currentPath = fs.path(args[2]) - loadImageList() - for i = 1, #imageList do - if args[2] == imageList[i] then currentImage = i; break end - end - else - ecs.error("Invalid arguments!") - return - end -else - loadImageList() -end - -drawAll() - -while true do - local e = {event.pull()} - if e[1] == "touch" then - - if enableSlideShow then - showGUI = true - enableSlideShow = false - if _G.imageViewerSlideShowTimer then event.cancel(_G.imageViewerSlideShowTimer) end - drawAll() - end - - if clicked(e[3], e[4], obj.arrowLeft) then - press(obj.arrowLeft[1] - 1, obj.arrowLeft[2] - 1) - prevImage() - elseif clicked(e[3], e[4], obj.play) then - press(obj.play[1] - 1, obj.play[2] - 1) - showGUI = false - obj = {} - enableSlideShowDro4er() - drawAll() - elseif clicked(e[3], e[4], obj.arrowRight) then - press(obj.arrowRight[1] - 1, obj.arrowRight[2] - 1) - nextImage() - elseif clicked(e[3], e[4], obj.wallpaper) then - press(obj.wallpaper[1] - 1, obj.wallpaper[2] - 1) - buffer.clear(0x262626) - buffer.draw() - ecs.createShortCut("MineOS/System/OS/Wallpaper.lnk", imageList[currentImage]) - computer.pushSignal("MineOSCore", "updateWallpaper") - return - elseif (e[3] >= 2 and e[3] <= 3 and e[4] == 1) then - buffer.text(2, 1, ecs.colors.blue, "⬤") - buffer.draw() - os.sleep(0.2) - buffer.clear(0x262626) - buffer.draw() - return - end - end -end - - - - - - - - - - - - - - - diff --git a/Applications/Viewer/arrowLeft.pic b/Applications/Viewer/arrowLeft.pic deleted file mode 100644 index b4c6dbeeb4e250a5d2001aab3c9d4eed67f15275..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 155 zcmX|)!3~2j5Cm`c)`2o;1tM9>D-XY}8;~d=N^AUSgxHqI?`E{S_kO;zM$Jf9!TDV? zj>Tw^oar%)0jc?|dsu*I{Q0wY-tQiuNbKFhDZ>`4EeGg#MCi+ds%zH_SV7IU&qt81 Gpu2vmz#BCH diff --git a/Applications/Viewer/arrowRight.pic b/Applications/Viewer/arrowRight.pic deleted file mode 100644 index dd08f52a0252f848be35ab9250e27e854537ec83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 155 zcmX|)yA8uI3p?|WHt9$BoPJJ9q;fC+?KU!(Fyiz_?;rcZJ)X=x_`Eq7U H&SL2grp_BR diff --git a/Applications/Viewer/play.pic b/Applications/Viewer/play.pic deleted file mode 100644 index 56a7ec21b81db570b7b3e130c52060d4f02ec480..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 161 zcmX|)!3~2z3{K0Mbdk^0=3~2@)klDKI6_0NnY(Yc;c8`Mqw%CY#|`#sBvy zI2WM=)B~p7?di}QhtA)JHTifp(01dMuL4=5EeJ4MlW>?1;}k})hh~fhrk??+Xv_Zp J3{owqYB$qD9IF5T diff --git a/Applications/Weather/Cloudy.pic b/Applications/Weather.app/Cloudy.pic similarity index 100% rename from Applications/Weather/Cloudy.pic rename to Applications/Weather.app/Cloudy.pic diff --git a/Applications/Weather/Foggy.pic b/Applications/Weather.app/Foggy.pic similarity index 100% rename from Applications/Weather/Foggy.pic rename to Applications/Weather.app/Foggy.pic diff --git a/Applications/Weather/Icon.pic b/Applications/Weather.app/Icon.pic similarity index 100% rename from Applications/Weather/Icon.pic rename to Applications/Weather.app/Icon.pic diff --git a/Applications/Weather/Weather.lua b/Applications/Weather.app/Main.lua similarity index 66% rename from Applications/Weather/Weather.lua rename to Applications/Weather.app/Main.lua index 0b8cb470..be7ff312 100755 --- a/Applications/Weather/Weather.lua +++ b/Applications/Weather.app/Main.lua @@ -1,27 +1,26 @@ -require("advancedLua") -local web = require("web") -local json = require("json") -local fs = require("filesystem") +local text = require("Text") +local number = require("Number") +local internet = require("Internet") +local json = require("JSON") +local fs = require("Filesystem") local bigLetters = require("bigLetters") -local buffer = require("doubleBuffering") -local image = require("image") -local unicode = require("unicode") -local component = require("component") +local screen = require("Screen") +local image = require("Image") local GUI = require("GUI") -local MineOSCore = require("MineOSCore") -local MineOSInterface = require("MineOSInterface") -local MineOSPaths = require("MineOSPaths") +local system = require("System") +local filesystem = require("Filesystem") +local paths = require("Paths") -------------------------------------------------------------------------------------------------------- -local application, window = MineOSInterface.addWindow(GUI.filledWindow(1, 1, 130, 30, 0)) +local workspace, window = system.addWindow(GUI.filledWindow(1, 1, 130, 30, 0)) window.backgroundPanel.colors.transparency = 0.2 local weatherContainer = window:addChild(GUI.container(1, 1, 1, 23)) -local configPath = MineOSPaths.applicationData .. "Weather/Config.cfg" -local resources = MineOSCore.getCurrentScriptDirectory() +local configPath = paths.user.applicationData .. "Weather/Config.cfg" +local resources = filesystem.path(system.getCurrentScript()) local weatherIcons = { sunny = image.load(resources .. "Sunny.pic"), sunnyAndCloudy = image.load(resources .. "Icon.pic"), @@ -60,9 +59,9 @@ local function newWeather(x, y, day) type = "sunnyAndCloudy" end - local temp = math.round(day.temp.min) .. " / " .. math.round(day.temp.max) .. " °C" - local pressure = math.round(day.pressure / 1.33322387415) .. " mm Hg" - local humidity = math.round(day.humidity) .. "%" + local temp = number.round(day.temp.min) .. " / " .. number.round(day.temp.max) .. " °C" + local pressure = number.round(day.pressure / 1.33322387415) .. " mm Hg" + local humidity = number.round(day.humidity) .. "%" local winds = { [0] = "N", [1] = "NE", @@ -74,15 +73,15 @@ local function newWeather(x, y, day) [7] = "NW", [8] = "N", } - local wind = day.speed .. " m/s, " .. (winds[math.round(day.deg / 45)] or "N/A") + local wind = day.speed .. " m/s, " .. (winds[number.round(day.deg / 45)] or "N/A") local function centerText(y, color, text) - buffer.drawText(math.floor(object.x + object.width / 2 - unicode.len(text) / 2), y, color, text) + screen.drawText(math.floor(object.x + object.width / 2 - unicode.len(text) / 2), y, color, text) end object.draw = function() centerText(object.y, 0xFFFFFF, os.date("%a", day.dt)) - buffer.drawImage(object.x + 3, object.y + 2, weatherIcons[type]) + screen.drawImage(object.x + 3, object.y + 2, weatherIcons[type]) centerText(object.y + 7, 0xFFFFFF, temp) centerText(object.y + 8, 0xDDDDDD, wind) centerText(object.y + 9, 0xBBBBBB, pressure) @@ -93,7 +92,7 @@ local function newWeather(x, y, day) end local function updateForecast() - local result, reason = web.request("http://api.openweathermap.org/data/2.5/forecast/daily?&appid=98ba4333281c6d0711ca78d2d0481c3d&units=metric&cnt=17&q=" .. web.encode(config.lastCityName)) + local result, reason = internet.request("http://api.openweathermap.org/data/2.5/forecast/daily?&appid=98ba4333281c6d0711ca78d2d0481c3d&units=metric&cnt=17&q=" .. internet.encode(config.lastCityName)) if result then result = json.decode(result) @@ -104,9 +103,9 @@ local function updateForecast() local currentDay = result.list[1] local object = weatherContainer:addChild(GUI.object(x + 2, y, 40, 8)) object.draw = function() - bigLetters.drawText(object.x, object.y, 0xFFFFFF, math.round((currentDay.temp.max + currentDay.temp.min) / 2) .. "°") - buffer.drawText(object.x, object.y + 6, 0xFFFFFF, result.city.name .. ", " .. result.city.country) - buffer.drawText(object.x, object.y + 7, 0xFFFFFF, "Population: " .. math.shorten(result.city.population, 2)) + bigLetters.drawText(object.x, object.y, 0xFFFFFF, number.round((currentDay.temp.max + currentDay.temp.min) / 2) .. "°") + screen.drawText(object.x, object.y + 6, 0xFFFFFF, result.city.name .. ", " .. result.city.country) + screen.drawText(object.x, object.y + 7, 0xFFFFFF, "Population: " .. number.shorten(result.city.population, 2)) end y = y + object.height + 1 @@ -124,8 +123,8 @@ local function updateForecast() x = x + object.width + 2 end - MineOSInterface.application:draw() - table.toFile(configPath, config) + workspace:draw() + filesystem.writeTable(configPath, config) else GUI.alert(result.message) end @@ -146,10 +145,10 @@ window.onResize = function(width, height) end window:resize(window.width, window.height) -MineOSInterface.application:draw() +workspace:draw() if fs.exists(configPath) then - config = table.fromFile(configPath) + config = filesystem.readTable(configPath) end updateForecast() diff --git a/Applications/Weather/Rainy.pic b/Applications/Weather.app/Rainy.pic similarity index 100% rename from Applications/Weather/Rainy.pic rename to Applications/Weather.app/Rainy.pic diff --git a/Applications/Weather/Snowy.pic b/Applications/Weather.app/Snowy.pic similarity index 100% rename from Applications/Weather/Snowy.pic rename to Applications/Weather.app/Snowy.pic diff --git a/Applications/Weather/Stormy.pic b/Applications/Weather.app/Stormy.pic similarity index 100% rename from Applications/Weather/Stormy.pic rename to Applications/Weather.app/Stormy.pic diff --git a/Applications/Weather/Sunny.pic b/Applications/Weather.app/Sunny.pic similarity index 100% rename from Applications/Weather/Sunny.pic rename to Applications/Weather.app/Sunny.pic diff --git a/Applications/archive.lua b/Applications/archive.lua deleted file mode 100644 index 143c972c..00000000 --- a/Applications/archive.lua +++ /dev/null @@ -1,71 +0,0 @@ -local archive = require("lib/archive") -local shell = require("shell") -local fs = require("filesystem") - ------------------------------------------------------------------------------------------------------------------------------------- - -local args, options = shell.parse(...) - -if not options.q then - archive.debugMode = true -end - -local function debug(text) - if not options.q then print(text) end -end - -if args[1] == "pack" then - if not args[2] or not args[3] then - debug(" ") - debug("Использование: archive pack <имя архива> <архивируемая папка>") - debug(" ") - return - end - debug(" ") - debug("Упаковка пакета начата") - debug(" ") - archive.pack(args[2], args[3]) - debug(" ") - debug("Упаковка пакета завершена, файл сохранен как \"" .. args[2] .. "\", его размер составил " .. math.ceil(fs.size(args[2]) / 1024) .. "КБ") - debug(" ") -elseif args[1] == "unpack" then - if not args[2] or not args[3] then - debug(" ") - debug("Использование: archive unpack <путь к архиву> <папка для сохранения файлов>") - debug(" ") - return - end - debug(" ") - debug("Распаковка пакета начата") - debug(" ") - archive.unpack(args[2], args[3]) - debug(" ") - debug("Распаковка пакета \"" .. args[2] .. "\" завершена") - debug(" ") -elseif args[1] == "download" or args[1] == "get" then - if not args[2] or not args[3] then - debug(" ") - debug("Использование: archive download <папка для сохранения файлов>") - debug(" ") - return - end - debug(" ") - debug("Загрузка файла по ссылке \"" .. args[2] .. "\"") - shell.execute("wget " .. args[2] .. " TempFile.pkg -fq") - debug(" ") - debug("Распаковка загруженного пакета") - archive.unpack("TempFile.pkg", args[3]) - shell.execute("rm TempFile.pkg") - debug(" ") - debug("Пакет \"" .. args[2] .. "\" был успешно загружен и распакован") - debug(" ") -else - debug(" ") - debug("Использование: archive ...") - debug(" ") - return -end - -archive.debugMode = false - ------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Applications/clear.lua b/Applications/clear.lua deleted file mode 100644 index 19f6961f..00000000 --- a/Applications/clear.lua +++ /dev/null @@ -1,8 +0,0 @@ - -local gpu = require("component").gpu -gpu.setBackground(0x1B1B1B) -gpu.setForeground(0xEEEEEE) - -local width, height = gpu.getResolution() -gpu.fill(1, 1, width, height, " ") -require("term").setCursor(1, 1) \ No newline at end of file diff --git a/Applications/draw.lua b/Applications/draw.lua deleted file mode 100644 index f3188353..00000000 --- a/Applications/draw.lua +++ /dev/null @@ -1,20 +0,0 @@ - -local args = {...} -local filesystem = require("filesystem") -local image = require("image") -local buffer = require("doubleBuffering") - -if args[1] then - if filesystem.exists(args[1]) then - -- Очищаем экранный буфер черным цветом - buffer.clear(0x000000) - -- Загружаем и рисуем изображение в буфер - buffer.image(1, 1, image.load(args[1])) - -- Отрисовываем содержимое буфера в принудительном режиме - buffer.draw(true) - else - print("Файл \"" .. tostring(args[1]) .. "\" не существует") - end -else - print("Использование: draw <путь к изображению>") -end diff --git a/EFI/Full.lua b/EFI/Full.lua index f3804674..b2a606ec 100644 --- a/EFI/Full.lua +++ b/EFI/Full.lua @@ -1,22 +1,14 @@ +local stringsMain, stringsInit, stringsChangeLabel, stringKeyDown, stringsFilesystem, colorsTitle, colorsBackground, colorsText, colorsSelectionBackground, colorsSelectionText, componentProxy, componentList, pullSignal, uptime, tableInsert, mathMax, mathMin, mathHuge, mathFloor = "MineOS EFI", "/OS.lua", "Change label", "key_down", "filesystem", 0x2D2D2D, 0xE1E1E1, 0x878787, 0x878787, 0xE1E1E1, component.proxy, component.list, computer.pullSignal, computer.uptime, table.insert, math.max, math.min, math.huge, math.floor --- local component, computer, unicode = require("component"), require("computer"), require("unicode") +local eeprom, gpu, internetAddress = componentProxy(componentList("eeprom")()), componentProxy(componentList("gpu")()), componentList("internet")() -local stringsMain, stringsBootFromURL, stringsChangeLabel, stringKeyDown, stringsInit, stringsFilesystem, componentProxy, componentList, pullSignal, uptime, tableInsert, mathMax, mathMin, mathHuge, mathFloor = "MineOS EFI", "Internet recovery", "Change label", "key_down", "/init.lua", "filesystem", component.proxy, component.list, computer.pullSignal, computer.uptime, table.insert, math.max, math.min, math.huge, math.floor -local colorsTitle, colorsBackground, colorsText, colorsSelectionBackground, colorsSelectionText, eeprom, gpu, internetAddress = 1, 0, 1, 1, 0, componentProxy(componentList("eeprom")()), componentProxy(componentList("gpu")()), componentList("internet")() +local shutdown, gpuSet, gpuFill, eepromSetData, eepromGetData, screenWidth, screenHeight, curentBackground, currentForeground = computer.shutdown, gpu.set, gpu.fill, eeprom.setData, eeprom.getData, gpu.getResolution() -gpu.bind(componentList("screen")(), true) - -local shutdown, gpuSet, gpuFill, eepromSetData, eepromGetData, depth, screenWidth, screenHeight, curentBackground, currentForeground, NIL = computer.shutdown, gpu.set, gpu.fill, eeprom.setData, eeprom.getData, gpu.getDepth(), gpu.getResolution() -computer.getBootAddress, computer.setBootAddress = eepromGetData, eepromSetData - -if depth == 4 then - colorsTitle, colorsBackground, colorsText, colorsSelectionBackground, colorsSelectionText = 0x333333, 0xFFFFFF, 0x333333, 0x333333, 0xFFFFFF -elseif depth == 8 then - colorsTitle, colorsBackground, colorsText, colorsSelectionBackground, colorsSelectionText = 0x2D2D2D, 0xE1E1E1, 0x878787, 0x878787, 0xE1E1E1 -end - -local setBackground, setForeground, restrict = +local resetColors, setBackground, setForeground, restrict = + function() + curentBackground, currentForeground = nil, nil + end, function(color) if color ~= curentBackground then gpu.setBackground(color) @@ -57,8 +49,8 @@ local rectangle, centrizedText, menuElement = } end -local function title(yHalfer, titleText) - y = mathFloor(screenHeight / 2 - yHalfer / 2) +local function title(y, titleText) + y = mathFloor(screenHeight / 2 - y / 2) rectangle(1, 1, screenWidth, screenHeight, colorsBackground) centrizedText(y, colorsTitle, titleText) @@ -85,28 +77,35 @@ local function status(titleText, statusText, needWait) end end -local loadInit, menuBack, menu, input, netboot = +local function executeString(...) + local result, reason = load(...) + if result then + result, reason = xpcall(result, debug.traceback) + if result then + return + end + end + + resetColors() + status(stringsMain, reason, 1) +end + +local loadInit, menuBack, menu, input = function(proxy) status(stringsMain, "Booting from " .. proxy.address) - local data, chunk, handle, success, reason = "", "", proxy.open(stringsInit, "r") - while chunk do - data, chunk = data .. chunk, proxy.read(handle, mathHuge) - end + local handle, data, chunk, success, reason = proxy.open(stringsInit, "rb"), "", "" + repeat + chunk = proxy.read(handle, mathHuge) + data = data .. (chunk or "") + until not chunk + proxy.close(handle) - success, reason = load(data, "=" .. stringsInit) - if success then - success, reason = pcall(success) - if success then - return - end - end - - status(stringsMain, reason, 1) + executeString(data, "=" .. stringsInit) end, function() - return menuElement("Back", NIL, 1) + return menuElement("Back", nil, 1) end, function(titleText, elements) local spacing, selectedElement, maxLength = 2, 1, 0 @@ -156,7 +155,7 @@ local loadInit, menuBack, menu, input, netboot = while 1 do eblo = prefix .. text gpuFill(1, y, screenWidth, 1, " ") - gpuSetForeground(colorsText) + setForeground(colorsText) gpuSet(mathFloor(screenWidth / 2 - #eblo / 2), y, eblo .. (state and "█" or "")) eventData = {pullSignal(0.5)} @@ -179,49 +178,9 @@ local loadInit, menuBack, menu, input, netboot = state = not state end end - end, - function(url) - local runReason, data, handle, result, reason = - function(text) - status(stringsBootFromURL, "Internet boot failed: " .. text, 1) - end, - "", - componentProxy(internetAddress).request(url) - - if handle then - status(stringsBootFromURL, "Downloading script...") - while 1 do - result, reason = handle.read(mathHuge) - if result then - data = data .. result - else - handle:close() - - if reason then - runReason(reason) - else - result, reason = load(data) - if result then - eepromSetData("#" .. url) - result, reason = pcall(result) - if result then - return - else - runReason(reason) - end - else - runReason(reason) - end - end - - break - end - end - else - runReason("invalid URL-address") - end end +gpu.bind(componentList("screen")(), true) status(stringsMain, "Hold Alt to show boot options menu") local deadline, eventData = uptime() + 1 @@ -283,8 +242,32 @@ while uptime() < deadline do } if internetAddress then - tableInsert(utilities, 2, menuElement(stringsBootFromURL, function() - netboot(input(title(2, stringsBootFromURL), "Enter URL: ")) + tableInsert(utilities, 2, menuElement("Internet recovery", function() + local handle, data, result, reason = componentProxy(internetAddress).request("https://raw.githubusercontent.com/IgorTimofeev/MineOSStandalone/master/Installer/Main.lua"), "" + + if handle then + status(stringsMain, "Downloading recovery script") + + while 1 do + result, reason = handle.read(mathHuge) + + if result then + data = data .. result + else + handle:close() + + if reason then + status(stringsMain, reason, 1) + else + executeString(data, "=string") + end + + break + end + end + else + status(stringsMain, "invalid URL-address", 1) + end end)) end @@ -292,29 +275,23 @@ while uptime() < deadline do end end -local data, proxy = eepromGetData() -if data:sub(1, 1) == "#" then - netboot(data:sub(2, -1)) +local proxy = componentProxy(eepromGetData()) +if proxy and proxy.exists(stringsInit) then + loadInit(proxy) else - proxy = componentProxy(data) - - if proxy and proxy.exists(stringsInit) then - loadInit(proxy) - else - for address in componentList(stringsFilesystem) do - proxy = componentProxy(address) - if proxy.exists(stringsInit) then - eepromSetData(address) - loadInit(proxy) - break - else - proxy = nil - end + for address in componentList(stringsFilesystem) do + proxy = componentProxy(address) + if proxy.exists(stringsInit) then + eepromSetData(address) + loadInit(proxy) + break + else + proxy = nil end + end - if not proxy then - status(stringsMain, "No bootable mediums found", 1) - end + if not proxy then + status(stringsMain, "No bootable mediums found", 1) end end diff --git a/EFI/Minified.lua b/EFI/Minified.lua index 7e5f924f..307bcbab 100644 --- a/EFI/Minified.lua +++ b/EFI/Minified.lua @@ -1 +1 @@ -local a,b,c,d,e,f,g,h,i,j,k,l,m,n,o="MineOS EFI","Internet recovery","Change label","key_down","/init.lua","filesystem",component.proxy,component.list,computer.pullSignal,computer.uptime,table.insert,math.max,math.min,math.huge,math.floor;local p,q,r,s,t,u,v,w=1,0,1,1,0,g(h("eeprom")()),g(h("gpu")()),h("internet")()v.bind(h("screen")(),true)local x,z,A,B,C,D,E,F,G,H,I=computer.shutdown,v.set,v.fill,u.setData,u.getData,v.getDepth(),v.getResolution()computer.getBootAddress,computer.setBootAddress=C,B;if D==4 then p,q,r,s,t=0x333333,0xFFFFFF,0x333333,0x333333,0xFFFFFF elseif D==8 then p,q,r,s,t=0x2D2D2D,0xE1E1E1,0x878787,0x878787,0xE1E1E1 end;local J,K,L=function(M)if M~=G then v.setBackground(M)G=M end end,function(M)if M~=H then v.setForeground(M)H=M end end,function(N,O,P)if#N1 then ak=ak-1 elseif am[4]==208 and ak<#ai then ak=ak+1 elseif am[4]==28 then if ai[ak].c then ai[ak].c()end;if ai[ak].b then return end end end end end,function(y,an)local N,ao,ap,am,aq="",true;while 1 do ap=an..N;A(1,y,E,1," ")gpuSetForeground(r)z(o(E/2-#ap/2),y,ap..(ao and"█"or""))am={i(0.5)}if am[1]==d then if am[4]==28 then return N elseif am[4]==14 then N=N:sub(1,-2)else aq=unicode.char(am[3])if aq:match("^[%w%d%p%s]+")then N=N..aq end end;ao=true elseif am[1]=="clipboard"then N=N..am[3]elseif not am[1]then ao=not ao end end end,function(ar)local as,ad,af,at,ah=function(N)a1(b,"Internet boot failed: "..N,1)end,"",g(w).request(ar)if af then a1(b,"Downloading script...")while 1 do at,ah=af.read(n)if at then ad=ad..at else af:close()if ah then as(ah)else at,ah=load(ad)if at then B("#"..ar)at,ah=pcall(at)if at then return else as(ah)end else as(ah)end end;break end end else as("invalid URL-address")end end;a1(a,"Hold Alt to show boot options menu")local au,am=j()+1;while j() "or" ")..L(az,10)..L(ac.spaceTotal()>1048576 and"HDD"or ac.spaceTotal()>65536 and"FDD"or"SYS",3)..L(aA and"R"or"R/W",3)..ay:sub(1,8).." "..L(string.format("%.2f",ac.spaceUsed()/ac.spaceTotal()*100).."%",6,1),function()local aB={a8()}if not aA then k(aB,1,S(c,function()ac.setLabel(aa(Z(2,c),"Enter new name: "))end,1))k(aB,2,S("Format",function()a1(a,"Formatting filesystem "..ay)for aC,aD in ipairs(ac.list("/"))do ac.remove(aD)end;a1(a,"Formatting finished",1)end,1))end;k(aB,1,S("Set as startup",function()B(ay)end,1))a9(az.." ("..ay..")",aB)end,1))end;a9("Select filesystem",aw)end),S("Shutdown",function()x()end),a8()}if w then k(av,2,S(b,function()ab(aa(Z(2,b),"Enter URL: "))end))end;a9(a,av)end end;local ad,ac=C()if ad:sub(1,1)=="#"then ab(ad:sub(2,-1))else ac=g(ad)if ac and ac.exists(e)then a7(ac)else for ay in h(f)do ac=g(ay)if ac.exists(e)then B(ay)a7(ac)break else ac=nil end end;if not ac then a1(a,"No bootable mediums found",1)end end end;x() \ No newline at end of file +local a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s="MineOS EFI","/OS.lua","Change label","key_down","filesystem",0x2D2D2D,0xE1E1E1,0x878787,0x878787,0xE1E1E1,component.proxy,component.list,computer.pullSignal,computer.uptime,table.insert,math.max,math.min,math.huge,math.floor;local t,u,v=k(l("eeprom")()),k(l("gpu")()),l("internet")()local w,x,y,z,A,B,C,D,E=computer.shutdown,u.set,u.fill,t.setData,t.getData,u.getResolution()local F,G,H,I=function()D,E=nil,nil end,function(J)if J~=D then u.setBackground(J)D=J end end,function(J)if J~=E then u.setForeground(J)E=J end end,function(K,L,M)if#K1 then ai=ai-1 elseif ak[4]==208 and ai<#ag then ai=ai+1 elseif ak[4]==28 then if ag[ai].c then ag[ai].c()end;if ag[ai].b then return end end end end end,function(R,al)local K,am,an,ak,ao="",true;while 1 do an=al..K;y(1,R,B,1," ")H(h)x(s(B/2-#an/2),R,an..(am and"█"or""))ak={m(0.5)}if ak[1]==d then if ak[4]==28 then return K elseif ak[4]==14 then K=K:sub(1,-2)else ao=unicode.char(ak[3])if ao:match("^[%w%d%p%s]+")then K=K..ao end end;am=true elseif ak[1]=="clipboard"then K=K..ak[3]elseif not ak[1]then am=not am end end end;u.bind(l("screen")(),true)Z(a,"Hold Alt to show boot options menu")local ap,ak=n()+1;while n() "or" ")..I(au,10)..I(ab.spaceTotal()>1048576 and"HDD"or ab.spaceTotal()>65536 and"FDD"or"SYS",3)..I(av and"R"or"R/W",3)..at:sub(1,8).." "..I(string.format("%.2f",ab.spaceUsed()/ab.spaceTotal()*100).."%",6,1),function()local aw={a8()}if not av then o(aw,1,P(c,function()ab.setLabel(aa(X(2,c),"Enter new name: "))end,1))o(aw,2,P("Format",function()Z(a,"Formatting filesystem "..at)for ax,ay in ipairs(ab.list("/"))do ab.remove(ay)end;Z(a,"Formatting finished",1)end,1))end;o(aw,1,P("Set as startup",function()z(at)end,1))a9(au.." ("..at..")",aw)end,1))end;a9("Select filesystem",ar)end),P("Shutdown",function()w()end),a8()}if v then o(aq,2,P("Internet recovery",function()local ac,ad,a5,a6=k(v).request("https://raw.githubusercontent.com/IgorTimofeev/MineOSStandalone/master/Installer/Main.lua"),""if ac then Z(a,"Downloading recovery script")while 1 do a5,a6=ac.read(r)if a5 then ad=ad..a5 else ac:close()if a6 then Z(a,a6,1)else a4(ad,"=string")end;break end end else Z(a,"invalid URL-address",1)end end))end;a9(a,aq)end end;local ab=k(A())if ab and ab.exists(b)then a7(ab)else for at in l(e)do ab=k(at)if ab.exists(b)then z(at)a7(ab)break else ab=nil end end;if not ab then Z(a,"No bootable mediums found",1)end end;w() \ No newline at end of file diff --git a/Extensions/3dm/ContextMenu.lua b/Extensions/3dm/ContextMenu.lua index c6ac58d8..3ead5ccd 100644 --- a/Extensions/3dm/ContextMenu.lua +++ b/Extensions/3dm/ContextMenu.lua @@ -1,8 +1,9 @@ -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") -local MineOSInterface = require("MineOSInterface") -local icon, menu = select(1, ...), select(2, ...) -menu:addItem(MineOSCore.localization.edit).onTouch = function() - MineOSInterface.safeLaunch(MineOSPaths.editor, icon.path) +local paths = require("Paths") +local system = require("System") + +local workspace, icon, menu = select(1, ...), select(2, ...), select(3, ...) + +menu:addItem(system.localization.edit).onTouch = function() + system.execute(paths.editor, icon.path) end \ No newline at end of file diff --git a/Extensions/Arc/Launcher.lua b/Extensions/Arc/Launcher.lua index b0fc3c88..8f588bd4 100755 --- a/Extensions/Arc/Launcher.lua +++ b/Extensions/Arc/Launcher.lua @@ -1,8 +1,8 @@ local path = select(1, ...) -local success, reason = require("archive").unpack(path, require("filesystem").path(path)) +local success, reason = require("Archive").unpack(path, require("Filesystem").path(path)) if not success then require("GUI").alert(reason) end -require("computer").pushSignal("MineOSCore", "updateFileList") \ No newline at end of file +computer.pushSignal("system", "updateFileList") \ No newline at end of file diff --git a/Extensions/Lua/ContextMenu.lua b/Extensions/Lua/ContextMenu.lua index f35bf325..93424c08 100755 --- a/Extensions/Lua/ContextMenu.lua +++ b/Extensions/Lua/ContextMenu.lua @@ -1,31 +1,28 @@ -local component = require("component") -local computer = require("computer") -local fs = require("filesystem") -local GUI = require("GUI") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") -local MineOSInterface = require("MineOSInterface") -local icon, menu = select(1, ...), select(2, ...) -menu:addItem(MineOSCore.localization.edit).onTouch = function() - MineOSInterface.safeLaunch(MineOSPaths.editor, icon.path) +local filesystem = require("Filesystem") +local GUI = require("GUI") +local paths = require("Paths") +local system = require("System") + +local workspace, icon, menu = select(1, ...), select(2, ...), select(3, ...) + +menu:addItem(system.localization.edit).onTouch = function() + system.execute(paths.system.applicationMineCodeIDE, icon.path) end menu:addSeparator() -menu:addItem(MineOSCore.localization.launchWithArguments).onTouch = function() - MineOSInterface.launchWithArguments(MineOSInterface.mainContainer, icon.path, true) +menu:addItem(system.localization.launchWithArguments).onTouch = function() + system.launchWithArguments(workspace, icon.path) end -menu:addItem(MineOSCore.localization.flashEEPROM, not component.isAvailable("eeprom") or fs.size(icon.path) > 4096).onTouch = function() - local container = MineOSInterface.addBackgroundContainer(MineOSInterface.mainContainer, MineOSCore.localization.flashEEPROM) - container.layout:addChild(GUI.label(1, 1, container.width, 1, 0x969696, MineOSCore.localization.flashingEEPROM .. "...")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) - MineOSInterface.mainContainer:drawOnScreen() +menu:addItem(system.localization.flashEEPROM, not component.isAvailable("eeprom") or filesystem.size(icon.path) > 4096).onTouch = function() + local container = GUI.addBackgroundContainer(workspace, true, true, system.localization.flashEEPROM) + container.layout:addChild(GUI.label(1, 1, container.width, 1, 0x969696, system.localization.flashingEEPROM .. "...")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + workspace:draw() - local file = io.open(icon.path, "r") - component.eeprom.set(file:read("*a")) - file:close() + component.get("eeprom").set(filesystem.read(icon.path)) container:remove() - MineOSInterface.mainContainer:drawOnScreen() -end \ No newline at end of file + workspace:draw() +end diff --git a/Extensions/Lua/Launcher.lua b/Extensions/Lua/Launcher.lua index dafb76ca..d42379fc 100755 --- a/Extensions/Lua/Launcher.lua +++ b/Extensions/Lua/Launcher.lua @@ -1,8 +1,2 @@ -local args = {...} -local MineOSInterface = require("MineOSInterface") - -MineOSInterface.clearTerminal() -if MineOSInterface.safeLaunch(args[1]) then - MineOSInterface.waitForPressingAnyKey() -end +require("System").execute(select(1, ...)) diff --git a/Extensions/Pic/ContextMenu.lua b/Extensions/Pic/ContextMenu.lua index c14ab9cc..c7df9fa7 100755 --- a/Extensions/Pic/ContextMenu.lua +++ b/Extensions/Pic/ContextMenu.lua @@ -1,10 +1,14 @@ -local computer = require("computer") -local MineOSCore = require("MineOSCore") -local icon, menu = select(1, ...), select(2, ...) -menu:addItem(MineOSCore.localization.setAsWallpaper).onTouch = function() - MineOSCore.properties.wallpaperEnabled = true - MineOSCore.properties.wallpaper = icon.path - MineOSCore.saveProperties() - computer.pushSignal("MineOSCore", "updateWallpaper") +local system = require("System") + +local workspace, icon, menu = select(1, ...), select(2, ...), select(3, ...) + +menu:addItem(system.localization.setAsWallpaper).onTouch = function() + system.properties.interfaceWallpaperEnabled = true + system.properties.interfaceWallpaperPath = icon.path + + system.updateWallpaper() + workspace:draw() + + system.saveProperties() end diff --git a/Files.cfg b/Files.cfg deleted file mode 100644 index 53e9b308..00000000 --- a/Files.cfg +++ /dev/null @@ -1,769 +0,0 @@ -{ - preInstall = { - { - path="/lib/advancedLua.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/AdvancedLua/master/AdvancedLua.lua", - fileID=93, - }, - { - path="/lib/color.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/Color/master/Color.lua", - fileID=92, - }, - { - path="/lib/FormatModules/OCIF.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/Image/master/OCIF.lua", - }, - { - path="/lib/image.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/Image/master/Image.lua", - fileID=73, - }, - { - path="/lib/doubleBuffering.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/DoubleBuffering/master/DoubleBuffering.lua", - fileID=97, - }, - { - path="/lib/GUI.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/GUI/master/GUI.lua", - fileID=100, - }, - { - path="/lib/MineOSCore.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/MineOSCore.lua", - fileID=108, - }, - { - path="/lib/MineOSPaths.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/MineOSPaths.lua", - fileID=110, - }, - { - path="/lib/web.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/web.lua", - fileID=101, - }, - { - path="/lib/scale.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/scale.lua", - fileID=505, - }, - }, - localizations = { - { - path="/MineOS/System/Localizations/English.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Localizations/OS/English.lang", - fileID=554, - }, - { - path="/MineOS/System/Localizations/French.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Localizations/OS/French.lang", - fileID=848, - }, - { - path="/MineOS/System/Localizations/Russian.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Localizations/OS/Russian.lang", - fileID=553, - }, - { - path="/MineOS/System/Localizations/Ukrainian.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Localizations/OS/Ukrainian.lang", - fileID=932, - }, - }, - duringInstall = { - -- Либы - { - path="/lib/MineOSNetwork.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/MineOSNetwork.lua", - fileID=253, - }, - { - path="/lib/MineOSInterface.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/MineOSInterface.lua", - fileID=109, - }, - -- { - -- path="/lib/event.lua", - -- url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/event.lua", - -- fileID=102, - -- }, - { - path="/lib/FormatModules/OCAF.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/FormatModules/OCAF.lua", - }, - { - path="/lib/archive.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/archive.lua", - fileID=94, - }, - { - path="/lib/SHA2.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/SHA2.lua", - fileID=104, - }, - { - path="/lib/ECSAPI.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/ECSAPI.lua", - fileID=252, - }, - -- Скрипты - { - path="/bin/OS.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/OS.lua", - fileID=106, - }, - -- Ассоциации контекстного меню - { - path="/MineOS/System/Extensions/Lua/ContextMenu.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Extensions/Lua/ContextMenu.lua", - fileID=150, - }, - { - path="/MineOS/System/Extensions/Lua/Launcher.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Extensions/Lua/Launcher.lua", - fileID=151, - }, - { - path="/MineOS/System/Extensions/Pic/ContextMenu.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Extensions/Pic/ContextMenu.lua", - fileID=152, - }, - { - path="/MineOS/System/Extensions/Arc/Launcher.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Extensions/Arc/Launcher.lua", - fileID=153, - }, - { - path="/MineOS/System/Extensions/3dm/ContextMenu.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Extensions/3dm/ContextMenu.lua", - fileID=870, - }, - -- Системные иконки - { - path="/MineOS/System/Icons/Application.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/Application.pic", - fileID=131, - }, - { - path="/MineOS/System/Icons/3DModel.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/3DModel.pic", - fileID=132, - }, - { - path="/MineOS/System/Icons/Computer.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/Computer.pic", - fileID=133, - }, - { - path="/MineOS/System/Icons/Robot.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/Robot.pic", - fileID=134, - }, - { - path="/MineOS/System/Icons/Tablet.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/Tablet.pic", - fileID=135, - }, - { - path="/MineOS/System/Icons/Pastebin.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/Pastebin.pic", - fileID=136, - }, - { - path="/MineOS/System/Icons/HDD.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/HDD.pic", - fileID=137, - }, - { - path="/MineOS/System/Icons/Floppy.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/Floppy.pic", - fileID=138, - }, - { - path="/MineOS/System/Icons/Steve.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/Steve.pic", - fileID=139, - }, - { - path="/MineOS/System/Icons/Folder.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/Folder.pic", - fileID=140, - }, - { - path="/MineOS/System/Icons/FileNotExists.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/FileNotExists.pic", - fileID=141, - }, - { - path="/MineOS/System/Icons/Script.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/Script.pic", - fileID=142, - }, - { - path="/MineOS/System/Icons/Text.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/Text.pic", - fileID=143, - }, - { - path="/MineOS/System/Icons/Config.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/Config.pic", - fileID=144, - }, - { - path="/MineOS/System/Icons/Image.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/Image.pic", - fileID=145, - }, - { - path="/MineOS/System/Icons/Lua.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/Lua.pic", - fileID=146, - }, - { - path="/MineOS/System/Icons/SampleIcon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/SampleIcon.pic", - fileID=147, - }, - { - path="/MineOS/System/Icons/Archive.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/Archive.pic", - fileID=148, - }, - { - path="/MineOS/System/Icons/Trash.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Icons/Trash.pic", - fileID=149, - }, - -- Скринсейверы - { - path="/MineOS/System/Screensavers/Matrix.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Screensavers/Matrix.lua", - }, - { - path="/MineOS/System/Screensavers/Mandala.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Screensavers/Mandala.lua", - }, - { - path="/MineOS/System/Screensavers/Clock.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Screensavers/Clock.lua", - }, - { - path="/MineOS/System/Screensavers/Lines.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Screensavers/Lines.lua", - }, - { - path="/MineOS/System/Screensavers/XCOM.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Screensavers/XCOM.lua", - }, - { - path="/MineOS/System/Screensavers/NyanCat.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Screensavers/NyanCat.lua", - }, - -- Приложения - { - path="/MineOS/Applications/App Market.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/AppMarket/Main.lua", - fileID=164, - }, - { - path="/MineOS/Applications/App Market.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/AppMarket/Icon.pic", - }, - { - path="/MineOS/Applications/App Market.app/Localizations/Russian.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/AppMarket/Localization/Russian.lang", - }, - { - path="/MineOS/Applications/App Market.app/Localizations/English.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/AppMarket/Localization/English.lang", - }, - { - path="/MineOS/Applications/App Market.app/Localizations/French.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/AppMarket/Localization/French.lang", - }, - { - path="/MineOS/Applications/App Market.app/Localizations/Ukrainian.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/AppMarket/Localization/Ukrainian.lang", - }, - -- - { - path="/MineOS/Applications/MineCode IDE.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/MineCodeIDE/Main.lua", - fileID=169, - }, - { - path="/MineOS/Applications/MineCode IDE.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/MineCodeIDE/Icon.pic", - }, - { - path="/MineOS/Applications/MineCode IDE.app/Localizations/Russian.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/MineCodeIDE/Localization/Russian.lang", - }, - { - path="/MineOS/Applications/MineCode IDE.app/Localizations/English.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/MineCodeIDE/Localization/English.lang", - }, - { - path="/MineOS/Applications/MineCode IDE.app/Localizations/French.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/MineCodeIDE/Localization/French.lang", - }, - { - path="/MineOS/Applications/MineCode IDE.app/Localizations/Ukrainian.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/MineCodeIDE/Localization/Ukrainian.lang", - }, - -- - { - path="/MineOS/Applications/Picture Edit.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/PictureEdit/Main.lua", - fileID=798, - }, - { - path="/MineOS/Applications/Picture Edit.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/PictureEdit/Icon.pic", - }, - { - path="/MineOS/Applications/Picture Edit.app/Tools/1.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/PictureEdit/Tools/1.lua", - }, - { - path="/MineOS/Applications/Picture Edit.app/Tools/2.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/PictureEdit/Tools/2.lua", - }, - { - path="/MineOS/Applications/Picture Edit.app/Tools/3.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/PictureEdit/Tools/3.lua", - }, - { - path="/MineOS/Applications/Picture Edit.app/Tools/4.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/PictureEdit/Tools/4.lua", - }, - { - path="/MineOS/Applications/Picture Edit.app/Tools/5.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/PictureEdit/Tools/5.lua", - }, - { - path="/MineOS/Applications/Picture Edit.app/Tools/6.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/PictureEdit/Tools/6.lua", - }, - { - path="/MineOS/Applications/Picture Edit.app/Tools/7.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/PictureEdit/Tools/7.lua", - }, - { - path="/MineOS/Applications/Picture Edit.app/Tools/8.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/PictureEdit/Tools/8.lua", - }, - -- - { - path="/MineOS/Applications/Finder.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Finder/Main.lua", - fileID=175, - }, - { - path="/MineOS/Applications/Finder.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Finder/Icon.pic", - }, - -- - { - path="/MineOS/Applications/Settings.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Main.lua", - fileID=909, - }, - { - path="/MineOS/Applications/Settings.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Icon.pic", - }, - { - path="/MineOS/Applications/Settings.app/Localizations/English.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Localizations/English.lang", - }, - { - path="/MineOS/Applications/Settings.app/Localizations/Russian.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Localizations/Russian.lang", - }, - { - path="/MineOS/Applications/Settings.app/Localizations/French.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Localizations/French.lang", - }, - { - path="/MineOS/Applications/Settings.app/Localizations/Ukrainian.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Localizations/Ukrainian.lang", - }, - { - path="/MineOS/Applications/Settings.app/Modules/0_Screen/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/0_Screen/Main.lua", - }, - { - path="/MineOS/Applications/Settings.app/Modules/0_Screen/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/0_Screen/Icon.pic", - }, - { - path="/MineOS/Applications/Settings.app/Modules/1_Wallpaper/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/1_Wallpaper/Main.lua", - }, - { - path="/MineOS/Applications/Settings.app/Modules/1_Wallpaper/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/1_Wallpaper/Icon.pic", - }, - { - path="/MineOS/Applications/Settings.app/Modules/2_Icons/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/2_Icons/Main.lua", - }, - { - path="/MineOS/Applications/Settings.app/Modules/2_Icons/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/2_Icons/Icon.pic", - }, - { - path="/MineOS/Applications/Settings.app/Modules/3_Tasks/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/3_Tasks/Main.lua", - }, - { - path="/MineOS/Applications/Settings.app/Modules/3_Tasks/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/3_Tasks/Icon.pic", - }, - { - path="/MineOS/Applications/Settings.app/Modules/4_Disks/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/4_Disks/Main.lua", - }, - { - path="/MineOS/Applications/Settings.app/Modules/4_Disks/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/4_Disks/Icon.pic", - }, - { - path="/MineOS/Applications/Settings.app/Modules/5_Network/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/5_Network/Main.lua", - }, - { - path="/MineOS/Applications/Settings.app/Modules/5_Network/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/5_Network/Icon.pic", - }, - { - path="/MineOS/Applications/Settings.app/Modules/6_Localizations/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/6_Localizations/Main.lua", - }, - { - path="/MineOS/Applications/Settings.app/Modules/6_Localizations/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/6_Localizations/Icon.pic", - }, - { - path="/MineOS/Applications/Settings.app/Modules/7_Time/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/7_Time/Main.lua", - }, - { - path="/MineOS/Applications/Settings.app/Modules/7_Time/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/7_Time/Icon.pic", - }, - { - path="/MineOS/Applications/Settings.app/Modules/8_System/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/8_System/Main.lua", - }, - { - path="/MineOS/Applications/Settings.app/Modules/8_System/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/8_System/Icon.pic", - }, - { - path="/MineOS/Applications/Settings.app/Modules/00_Users/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/00_Users/Main.lua", - }, - { - path="/MineOS/Applications/Settings.app/Modules/00_Users/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Settings/Modules/00_Users/Icon.pic", - }, - }, - optional = { - { - path="/lib/json.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/json.lua", - fileID=103, - }, - { - path="/lib/bigLetters.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/bigLetters.lua", - fileID=391, - }, - { - path="/lib/MeowEngine/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/MeowEngine/Main.lua", - fileID=340, - }, - { - path="/lib/OpenComputersGL/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/OpenComputersGL/Main.lua", - fileID=337, - }, - { - path="/lib/OpenComputersGL/Renderer.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/OpenComputersGL/Renderer.lua", - fileID=338, - }, - { - path="/lib/OpenComputersGL/Materials.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/OpenComputersGL/Materials.lua", - fileID=339, - }, - { - path="/lib/vector.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/lib/vector.lua", - fileID=520, - }, - -- 3D Print - { - path="/MineOS/Applications/3D Print.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/3DPrint/Main.lua", - fileID=859, - createShortcut = true, - }, - { - path="/MineOS/Applications/3D Print.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/3DPrint/Icon.pic", - }, - { - path="/MineOS/Applications/3D Print.app/Localizations/English.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/3DPrint/Localizations/English.lang", - }, - { - path="/MineOS/Applications/3D Print.app/Localizations/Russian.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/3DPrint/Localizations/Russian.lang", - }, - { - path="/MineOS/Applications/3D Print.app/Localizations/French.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/3DPrint/Localizations/French.lang", - }, - { - path="/MineOS/Applications/3D Print.app/Localizations/Ukrainian.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/3DPrint/Localizations/Ukrainian.lang", - }, - -- Weather - { - path="/MineOS/Applications/Weather.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Weather/Weather.lua", - fileID=240, - createShortcut = true, - }, - { - path="/MineOS/Applications/Weather.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Weather/Icon.pic", - }, - { - path="/MineOS/Applications/Weather.app/Sunny.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Weather/Sunny.pic", - }, - { - path="/MineOS/Applications/Weather.app/Cloudy.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Weather/Cloudy.pic", - }, - { - path="/MineOS/Applications/Weather.app/Rainy.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Weather/Rainy.pic", - }, - { - path="/MineOS/Applications/Weather.app/Foggy.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Weather/Foggy.pic", - }, - { - path="/MineOS/Applications/Weather.app/Snowy.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Weather/Snowy.pic", - }, - { - path="/MineOS/Applications/Weather.app/Stormy.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Weather/Stormy.pic", - }, - -- Calculator - { - path="/MineOS/Applications/Calculator.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Calculator/Main.lua", - fileID=1045, - createShortcut = true, - }, - { - path="/MineOS/Applications/Calculator.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Calculator/Icon.pic", - }, - -- Fuck the rain - { - path="/MineOS/Applications/Fuck the rain.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/FuckTheRain/FuckTheRain.lua", - fileID=506, - createShortcut = true, - }, - { - path="/MineOS/Applications/Fuck the rain.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/FuckTheRain/Icon.pic", - }, - -- Graph - { - path="/MineOS/Applications/Graph.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Graph2/Main.lua", - fileID=238, - createShortcut = true, - }, - { - path="/MineOS/Applications/Graph.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Graph2/Icon.pic", - }, - -- HEX - { - path="/MineOS/Applications/HEX.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/HEX/Main.lua", - fileID=248, - createShortcut = true, - }, - { - path="/MineOS/Applications/HEX.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/HEX/Icon.pic", - }, - -- Running string - { - path="/MineOS/Applications/RunningString.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/RunningString/RunningString.lua", - fileID=410, - createShortcut = true, - }, - { - path="/MineOS/Applications/RunningString.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/RunningString/Icon.pic", - }, - -- Palette - { - path="/MineOS/Applications/Palette.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Palette/Palette.lua", - fileID=250, - createShortcut = true, - }, - { - path="/MineOS/Applications/Palette.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Palette/Icon.pic", - }, - -- HoloClock - { - path="/MineOS/Applications/HoloClock.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/HoloClock/HoloClock.lua", - fileID=559, - createShortcut = true, - }, - { - path="/MineOS/Applications/HoloClock.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/HoloClock/Icon.pic", - }, - -- Symbols - { - path="/MineOS/Applications/Symbols.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Symbols/Main.lua", - fileID=853, - createShortcut = true, - }, - { - path="/MineOS/Applications/Symbols.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Symbols/Icon.pic", - }, - -- IRC - { - path="/MineOS/Applications/IRC.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/IRC/Main.lua", - fileID=856, - createShortcut = true, - }, - { - path="/MineOS/Applications/IRC.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/IRC/Icon.pic", - }, - -- Nanomachines - { - path="/MineOS/Applications/Nanomachines.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Nanomachines/Main.lua", - fileID=938, - createShortcut = true, - }, - { - path="/MineOS/Applications/Nanomachines.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Nanomachines/Icon.pic", - }, - { - path="/MineOS/Applications/Nanomachines.app/Localizations/English.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Nanomachines/Localizations/English.lang", - }, - { - path="/MineOS/Applications/Nanomachines.app/Localizations/Russian.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Nanomachines/Localizations/Russian.lang", - }, - { - path="/MineOS/Applications/Nanomachines.app/Localizations/French.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Nanomachines/Localizations/French.lang", - }, - { - path="/MineOS/Applications/Nanomachines.app/Localizations/Ukrainian.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/Nanomachines/Localizations/Ukrainian.lang", - }, - -- VK - { - path="/MineOS/Applications/VK.app/Main.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/VK/Main.lua", - fileID=952, - createShortcut = true, - }, - { - path="/MineOS/Applications/VK.app/Icon.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/VK/Icon.pic", - }, - { - path="/MineOS/Applications/VK.app/Localizations/English.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/VK/Localizations/English.lang", - }, - { - path="/MineOS/Applications/VK.app/Localizations/Russian.lang", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/VK/Localizations/Russian.lang", - }, - { - path="/MineOS/Applications/VK.app/Icons/Likes.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/VK/Icons/Likes.pic", - }, - { - path="/MineOS/Applications/VK.app/Icons/Reposts.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/VK/Icons/Reposts.pic", - }, - { - path="/MineOS/Applications/VK.app/Icons/Comments.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/VK/Icons/Comments.pic", - }, - { - path="/MineOS/Applications/VK.app/Icons/Views.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/VK/Icons/Views.pic", - }, - { - path="/MineOS/Applications/VK.app/Styles/Bright.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/VK/Styles/Bright.lua", - }, - { - path="/MineOS/Applications/VK.app/Styles/Dark.lua", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Applications/VK/Styles/Dark.lua", - }, - }, - wallpapers = { - { - path="/MineOS/Pictures/Raspberry.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Wallpapers/Raspberry.pic", - }, - { - path="/MineOS/Pictures/Ciri.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Wallpapers/Ciri.pic", - }, - { - path="/MineOS/Pictures/Girl.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Wallpapers/Girl.pic", - }, - { - path="/MineOS/Pictures/Space.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Wallpapers/Space.pic", - }, - { - path="/MineOS/Pictures/Road.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Wallpapers/Road.pic", - }, - { - path="/MineOS/Pictures/TyanSunset.pic", - url="https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Wallpapers/TyanSunset.pic", - }, - } -} \ No newline at end of file diff --git a/Icons/Computer.pic b/Icons/Computer.pic deleted file mode 100644 index a7c0cd2258032a4f25199e85f209939deabca007..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 120 zcmXYoyA6Oa5JS(tOUM|hSpx})ij^psArch}unr4k5x6KREcx@iFWVZEQABGwFhhVy swGgLTPERj1p%*DrAOw-Z{GSGZ^L-u`=-7J}W ze|&w?*u%dH>LLXxUNT^nXjEqgH!a(7y;}Y3{GxH`H(OWj>&{IVO9@pA&1s3 z`2Q-Pt%94YPUq-YDP!nRvyqGP6RXXWc`ZrRw)EVNlyIelCnc2RXVQ11IG6?D?cHLz z+pEVf(h{-Jcrv|zczl}87t7W9`Q`O(v)%1A9E7x<`uVs{f^e~9C5{0OFL6w8tP;lt z$0>1Ka5l+B#6;9&!iDVhgK#jcsyT|W)9Vk0qw#FMT5q;dtorFZJ7IRdC?-@mQLJ^u zSiK|V!kIj9M0f!d%Bs0->N#UR+LX+*pfl&0^6-Y9=Xm|q_EoBXiSs90&j{#KId E4?C2MU;qFB diff --git a/Icons/FileNotExists.pic b/Icons/FileNotExists.pic old mode 100644 new mode 100755 diff --git a/Icons/Finger.pic b/Icons/Finger.pic deleted file mode 100644 index 21d43984126627d0237108f02979b129b5a30b24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 418 zcmX|-M{Wc$5Cf+*K{<}nasRS_0iXQ;*Vt^$!Z(3nlSTc!zrU^-#P3Eg+Gq=uN$kgY zNu_hSYwKf*@<_%yA7WbC9-+ZwVuB5(!;!$1^Eevw%!Ca6xyZGRd0E8bAeY*vj40s_ zPAsiY6(C53gP6+Qx=sYURH%G}X(A$Vu^>3Zf^;QvMh5DbE--{PjLA@*DKEJ&&@yIN z>sD+?z=E{>FMoV7iO=|5V*}UKcDVfjyG3qp{gk{NvoB9DKi`5rsp&K}fn_K|0P!K? mGqe(5;-}HTF);LDCBSSd-5DNk9si3*qtfWOYmhu7mirI8ClxyY diff --git a/Icons/Idiot.pic b/Icons/Idiot.pic deleted file mode 100644 index d555f5144a7116f154d487d7888922e1218536a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 197 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 GLv21#q9)(~ diff --git a/Icons/OS_Logo.pic b/Icons/OS_Logo.pic deleted file mode 100644 index f6e8fcf7789ad27092787d0f07af20f0e721c4e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1372 zcmYjRyN(-25M=ed-}`=$3>2DFLIrn$n9wj_z>x!x8o7 zY5{}AsIleJI>Kb6fSgDsOV9N?1Os=4gF2d5Rx{qEL{Y-ky^Nl=wggbNOV&R2#w+Ei5YpxWaBJg7N4Dyrw ziimg?5sajQ_{>oxm@7r-1aaVfCL&)D5N>AAh1Yi?O05*G-F$?FKaygt4C)i3&C;8^ z@^<-I-uSut!i%{pfg)iQl15`FS4U{rU$C@1>=Xy8Iu?VBec%JzM~3GEx7Zq5ZylOI zRc}Q%OlP&1kwkFtJf?uic7rQedrfvVBLbbi|}v-KA`y|ZV>1e-&S`Ls3ofR zY{!K0BK9o76ksc!AUSLUy0i4yIA#`53YrhNuvYy^zd~ZQamA~>ut@TW>*O1gl}lcm;4RoF{uv%|Pf6(5W{fXvDT<_ghIbGJX(5(| z02l%fW5=c$K621lAIzRz+>KFG-C)o=j7e7Yu(8-=3}0E7eAb>y%U8sajsa6aiLlQI zR5R^FVRmp9X2*pq!-P=>tSz8hT zWG8VVR(g;!wyh#nUS;4k{gIDk33V3|lUOWLPFy4I{bB{_FeOsm z($ZwnJd@{tM--`S4%(xS)NBLIn+n-dwJub(S3bs0G6wQJ)2z;v&}X&@x4$vy95w}Y RMrZk7K=sTfLcQFJ^e^tvTDSlJ diff --git a/Icons/Pastebin.pic b/Icons/Pastebin.pic deleted file mode 100644 index 038d95bae062e1ea96acdb16a7d1e7a64840e50b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 185 zcmeZw_H<+8U}0on5GZ2QVm!sj!pM;cWc+7jWMnUeim( mR}fK`d(T_jmYJLIuoa&z@8MNG{WYk(Q0PE1)V{f?$A&-RGBZs8 diff --git a/Icons/Robot.pic b/Icons/Robot.pic deleted file mode 100644 index 107fdce4bcfd9c91bbc792ce91a039a0753d9ac3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 150 zcmW-a!3o1a3)Io=RFV5BdRgfP&ff+kt=6CH}OCh9>6g_VwQxa87*34RlLC2Wo>gmGtT Kr7rPAV3}`_)(~j` diff --git a/Icons/SampleIcon.pic b/Icons/SampleIcon.pic deleted file mode 100644 index 06a0bcaa7b1d2103e69b3ea3f1f696f51d8b8bad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 161 zcmXYqyA6Oa3`BFbd#tz#ci=vphwDH|4xw4Gu}VWtU;Y02 z>C0!@d;Lpchl1*HgErQY=MYnF+HM%9`FweKy)Nr^yYG5_clr0qL==zmZnqk{v(WdQ z>erKm6jy$%flG=KEXJCw22S}%e5A?kvGb3#&G6WBc$U(hpJy?>E3v!2JI~EO<@f(- zPYDO9L?wg8>UDEi39HA#SOTt5zq}?~8Y5I%DtP+SH>e2oT6#5|VJ&;4E$q z8)5TU8Ee22aZQ3L>+_|--0ETMbbtB?6*PJyjI1vL6Go0~)fvHhI5wiX053}V0mTjz zVfL5(aP!z0TfiEzBwVxf(MTVyp61S$f1#IFegparEf)X) diff --git a/Icons/Steve.pic b/Icons/Steve.pic deleted file mode 100644 index 5d8bcc4d04ae651e47125ec9deb7b0201f73daf1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 195 zcmZXO!41MN3`PAD$8p<0JVHn4kvmeQo|u7wn2N!`E&?I-_WZv6<=b^z#S}!CW<6vf7&@AChOB`M?bH%^#MX8NmPm diff --git a/Icons/Trash.pic b/Icons/Trash.pic old mode 100644 new mode 100755 index c36191aea1d0870b3f35cc9062331b3df83c9732..40aa1f5996e3d69765a4d3f13771da0253667381 GIT binary patch delta 94 zcmZo+Y+-cqclLB+<6vR>&%pL*VLKxO({)B5xXQ@H$nj{=Zbk;iHH^%RObS2-0~0e) qkcEYjiJjxo!ue1s_D73$OqA0XWME)?w77wBHDfR%3kx#~D;ogP9TU0$ delta 94 zcmZo+Y+;o1clLB+<6vR>&%ma@$iT?J#LUFV!NS7G#Ln?(+JuQN`sRk@(>5QSMl#0yATW_FQxai*Dm^)S6s?^#f>GSlEM);+T4e9L{lU4=FDMZf|9 zVqgHucqVdLlvTa!_rqa4p3axaQWKQWVbXWJ#<9vupt>=ewwtVo>Rf!g@^==$v((N; zeYLH#t2EH%d$M&jL%Eu9v+<4^9h<*gCdoh?rs#n1d<-xiQ+a0uEmkzSqLnSq#92=u z6lCC|=Hs@|4zts`W1gf)K&bn<_Lxs|)A`pB{EaAS2M KWz`}T?)D7?8Yo`? diff --git a/Icons/User.pic b/Icons/User.pic new file mode 100644 index 0000000000000000000000000000000000000000..3d0fe33e161a2f6c644728d8d1af28d9eba3e273 GIT binary patch literal 254 zcmXv}TMEK35X|hxG~y-t+j9tlUtY-%|Gh%nh+aSxLF_5Kz+OZrX}S;?CiB=mZ1=km zz5Lw9(i;w62w>@iju2=p-6|U`^#uZA=`jn$udF{8>_Nr7`qcVYWtn!ZY_K#HsDjvv zV;pC0iq5TVII^{aXYaT`v0*B#g8s`a5DhjiwB}g)RJe}v`Mg$o7O5b#83?DLf){=2 GhyDQ)i9S>S literal 0 HcmV?d00001 diff --git a/Installer.lua b/Installer.lua deleted file mode 100644 index 5b54b480..00000000 --- a/Installer.lua +++ /dev/null @@ -1,322 +0,0 @@ - -local fs = require("filesystem") -local component = require("component") -local computer = require("computer") -local unicode = require("unicode") -local shell = require("shell") -local gpu = component.gpu -local screen = component.screen - -local args, options = shell.parse(...) - ------------------------------------------------------------------------------------------------------------------------------------- - -local URLs = { - applicationList = "https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Files.cfg", - installerLocalization = "https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/Localizations/Installer/", - EFI = "https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/EFI/Minified.lua", - license = "https://raw.githubusercontent.com/IgorTimofeev/MineOS/master/LICENSE", -} - ------------------------------------------------------------------------------------------------------------------------------------- - -local reasons = {} - -if not _G._OSVERSION or tonumber(_G._OSVERSION:sub(8, 10)) < 1.5 then - table.insert(reasons, "Old version of OpenComputers mod detected: MineOS requires OpenComputers 1.5 or newer to work properly.") -end - -if component.isAvailable("tablet") then - table.insert(reasons, "Tablet PC detected: MineOS can't be installed on tablets.") -end - -if screen.setPrecise and screen.setPrecise(false) == nil then - table.insert(reasons, "Low-tier screen detected: MineOS requires Tier 3 screen to work properly.") -else - if gpu.maxResolution() < 160 then - table.insert(reasons, "Low-tier GPU detected: MineOS requires Tier 3 GPU to work properly.") - end -end - -if computer.totalMemory() < 2097152 then - table.insert(reasons, "Not enough RAM: MineOS requires at least 2MB (2x Tier 3.5 RAM modules) to work properly.") -end - -if #reasons > 0 and not options.skiphardwarecheck and not options.s then - print(" ") - for i = 1, #reasons do - print(reasons[i]) - print(" ") - end - - return -end - ------------------------------------------------------------------------------------------------------------------------------------- - -local fileVersions = {} - -local function storeFileVersion(application) - if application.fileID then - fileVersions[application.fileID] = { - path = application.path, - version = application.version or 1 - } - end -end - -local function unserializeFile(path) - local file = io.open(path, "r") - local data = require("serialization").unserialize(file:read("*a")) - file:close() - return data -end - -local function wget(url, path) - fs.makeDirectory(fs.path(path)) - shell.execute("wget " .. url .. " " .. path .. " -fq") -end - -print("Downloading MineOS file list...") -local path = "/MineOS/System/Files.cfg" -wget(URLs.applicationList, path) -applicationList = unserializeFile(path) -fs.remove(path) - -print(" ") - -for i = 1, #applicationList.preInstall do - print("Downloading library \"" .. fs.name(applicationList.preInstall[i].path) .. "\"") - wget(applicationList.preInstall[i].url, applicationList.preInstall[i].path) - storeFileVersion(applicationList.preInstall[i]) -end - ------------------------------------------------------------------------------------------------------------------------------------- - -print(" ") -print("Loading installer images...") -local image = require("image") - -local images = { - languages = [[3C100000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0068FF▄003CFF▄009EFF▄6D9900▄6D9E00▄6D9900▄6C6D00▄673700▄3C0000 0067FF▄0067FF▄0055FF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0055FFВ0055FFи0055FFб0055FFе0055FFр0055FFі0055FFт0055FFь0000FF 0055FFм0055FFо0055FFв0055FFу0000FF 0000FF 0000FF 0000FF 003DFF▄003DFF▄3D6D00▄689E00▄9EAC00▄9E6800▄993700▄379E00▄6D9E00▄6D3C00▄3C0000 6D3700▄67AA00▄808100▄810000 678000▄547E00▄360000 0054FF▄002AFF▄0000FF 0000FF 0000FF 007EFFC007EFFh007EFFo007EFFi007EFFs007EFFi007EFFs007EFFs007EFFe007EFFz0000FF 007EFFv007EFFo007EFFt007EFFr007EFFe0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 003CFF▄3D9E00▄6D0000 6D9E00▄0C6D00▄6D3D00▄D53C00▄D59E00▄D59E00▄D6D700▄6DD600▄D56D00▄817E00▄376600▄6C0000 6CAA00▄800000 AA0000 7E6600▄7E9700▄7E6600▄547E00▄2A5300▄002AFF▄0000FF 0000FF 0000FF 0000FF 0000FF 007EFFl007EFFa007EFFn007EFFg007EFFu007EFFe0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0029FFC0029FFa0029FFm0029FFb0029FFi0029FFa0029FFr0000FF 0029FFl0029FFa0000FF 0000FF 0000FF 0000FF 0000FF 3C0C00▄6C3700▄0C9E00▄6D6800▄680C00▄379E00▄3D3700▄0CCE00▄CEFE00▄3D3700▄D50C00▄9C3C00▄3DCD00▄6B6C00▄3B9D00▄6CCE00▄9D0000 CD9C00▄C89D00▄979800▄970000 669200▄660000 7E0000 535400▄295300▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0029FFl0029FFe0029FFn0029FFg0029FFu0029FFa0000FF 0000FF 0000FF 0000FF 0000FF 0D0C00▄0C3700▄3D0C00▄0C3C00▄3D0C00▄376D00▄379900▄3D0C00▄0C3700▄3CCD00▄F89D00▄9D6C00▄9D6B00▄9CC800▄CD9C00▄ABCD00▄CD6C00▄C70C00▄CECD00▄979C00▄370C00▄98C800▄980000 929700▄668000▄7F0000 545500▄2A5300▄0000FF 0000FF 0000FF 0000FF 0053FFW0053FFy0053FFb0053FFi0053FFe0053FFr0053FFz0000FF 0053FFs0053FFw0053FFó0053FFj0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 380C00▄0C3700▄0C3700▄370000 3C0C00▄6D0C00▄6DD700▄0C3700▄6BCD00▄3DCD00▄CD3D00▄9D3800▄6B3700▄0CCD00▄6B0C00▄CD9C00▄CD0C00▄0CC700▄07C800▄07C800▄9D0000 C79800▄079700▄979200▄7E9200▄7F7E00▄666100▄7E6600▄365500▄292A00▄0000FF 0000FF 0053FFj0053FFę0053FFz0053FFy0053FFk0000FF 0053FF(0053FFk0053FFu0053FFr0053FFw0053FFa0053FF)0055FFВ0055FFы0055FFб0055FFе0055FFр0055FFи0055FFт0055FFе0000FF 0055FFя0055FFз0055FFы0055FFк0000FF 003DFF▄6D3700▄3D3700▄0C0000 0C0000 370700▄0C0700▄370000 376D00▄F8D700▄3DD600▄3DCD00▄3DFE00▄3DF800▄CD3700▄0C0000 9C0C00▄370700▄970700▄C80700▄C79700▄C8C300▄C80000 986700▄7E9700▄936600▄939800▄7E6100▄7E5500▄540000 2A0000 0028FF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 373D00▄0C0000 0C6800▄0C3700▄0C0000 370C00▄6D0C00▄0CC800▄C8D700▄F3F800▄F90000 F9F300▄F90000 F80000 F9C800▄0CF900▄F9CE00▄F9CE00▄CE0000 C80000 C80700▄C39200▄C30000 C8C300▄06C800▄069800▄069800▄060000 360600▄545300▄2A0000 280000 0000FF 0000FF 0055FFا0055FFخ0055FFت0055FFر0000FF 0055FFل0055FFغ0055FFت0055FFك0000FF 0000FF 0000FF 0000FF 0080FFS0080FFc0080FFe0080FFg0080FFl0080FFi0000FF 0080FFl0080FFa0000FF 0000FF 0000FF 0000FF 0C0000 0C0000 3D0C00▄370000 370C00▄0C0700▄FEF900▄F90000 FEF900▄F8F300▄F90000 C8F900▄C8F900▄C8F900▄F9F300▄C8CE00▄D6C800▄C3C800▄CECD00▄CDC800▄C3C800▄079200▄C30700▄C89800▄C89800▄980000 980600▄060000 060000 362A00▄2A2800▄282900▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0080FFl0080FFi0080FFn0080FFg0080FFu0080FFa0000FF 0000FF 0000FF 0000FF 003CFF▀370000 373C00▄370700▄0C0000 070C00▄F8CD00▄F9CD00▄D7CD00▄FECD00▄F8C800▄F9F800▄C80000 D60000 F9CD00▄CE0000 C8CD00▄C3C800▄C80000 C80000 C8C700▄929D00▄079D00▄979300▄983700▄069200▄370600▄060000 060000 2A2900▄2A2800▄0028FF▀0000FF 0000FF 002AFFא002AFFת0000FF 002AFFה002AFFש002AFFפ002AFFה0000FF 002AFFש002AFFל002AFFך0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0C3C00▄370C00▄370C00▄370000 0C9E00▄9D3C00▄9DCE00▄D6CE00▄D7CD00▄D70000 F9CE00▄CDCE00▄C8CD00▄37CD00▄D6CD00▄C8CD00▄D5C700▄CE9D00▄C89C00▄AC9D00▄9DAA00▄AA9D00▄AA9700▄AA8100▄540600▄060000 063600▄062900▄2A2800▄280000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 002AFFב002AFFח002AFFר0000FF 0000FF 0000FF 0000FF 0000FF 0054FFV0054FFa0054FFl0054FFi0054FFt0054FFs0054FFe0000FF 0054FFk0054FFi0054FFe0054FFl0054FFi0000FF 0000FF 0000FF 376700▄0C3700▄070C00▄073700▄070000 9E0C00▄CE0700▄AB6800▄D66D00▄D56D00▄CE0C00▄D56C00▄C83B00▄9D6B00▄9DD500▄6C8100▄AAAC00▄97AB00▄9D9700▄970000 AA9700▄986B00▄360600▄060000 060000 060000 292800▄280000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0C0000 070000 070C00▄070000 070000 070000 6D6700▄070600▄370000 073700▄9D0600▄6C9D00▄816B00▄6C6600▄666B00▄816B00▄AA6600▄6C6B00▄976600▄986600▄063100▄060000 060000 062800▄282900▄280000 0000FF 0000FF 0000FF 007EFFW007EFFä007EFFh007EFFl007EFFe007EFFn0000FF 007EFFs007EFFi007EFFe0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0007FF▀370600▄370600▄370000 370600▄670600▄060000 060000 070600▄063700▄070C00▄6B0000 6B6600▄6B0000 6B6600▄363B00▄660000 660000 663600▄060000 062A00▄292800▄280000 0028FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 007EFFi007EFFh007EFFr007EFFe0000FF 007EFFs007EFFp007EFFr007EFFa007EFFc007EFFh007EFFe0000FF 0000FF 0000FFC0000FFh0000FFo0000FFo0000FFs0000FFe0000FF 0000FFl0000FFa0000FFn0000FFg0000FFu0000FFa0000FFg0000FFe0000FF 0000FF 0000FF 0006FF▀0037FF▀370600▄063700▄060000 060000 060000 060000 543700▄927E00▄660000 666100▄666100▄665500▄543600▄062900▄062900▄2A2800▄002AFF▀0029FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0036FF▀0006FF▀0031FF▀062900▄312900▄312800▄532800▄532800▄532800▄002AFF▀0029FF▀0028FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF ]], - OS = [[5A14AA0000 AA2900MAA2900iAA2900nAA2900eAA2900OAA2900SAA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AAAB00▄AB0000 AB0000 AB0000 ABAA00▄AA0000 ABAA00▄AB0000 ABAA00▄AB0000 AB0000 AB0000 ACAB00▄AC0000 ABAC00▄AB0000 AB0000 AB0000 AB0000 AB0000 AB0000 AB0000 AB0000 AB0000 AB0000 AB0000 ABAA00▄AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 ABAA00▄AAAB00▄AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 AA0000 280000 290000 290000 280000 280000 290000 280000 290000 290000 282900▄290000 290000 290000 292A00▄290000 290000 290000 290000 2A5300▄2A5300▄2A0000 545500▄7E0000 7E5400▄2A0000 536100▄2A5300▄290000 532900▄2A5300▄555400▄530000 555300▄7E7F00▄7F7E00▄819800▄7F8100▄7E8000▄535500▄530000 535400▄2A5300▄545300▄540000 540000 545300▄545300▄2A5300▄2A0000 2A0000 292A00▄290000 292A00▄290000 290000 290000 292800▄290000 282900▄280000 280000 290000 292800▄280000 280000 2A2900▄530000 2A0000 2A0000 290000 290000 290000 290000 290000 290000 280000 280000 280000 280000 280000 280000 280000 280000 280000 280000 280000 280000 280000 280000 280000 280000 290000 280000 534300.FF0000 FF1800▄FF1800▄FF0000 290000 290000 290000 290000 290000 29A700▀290000 290000 292A00▄535400▄532A00▄002200▀000000 00E300▄000000 002200▀669200▄669200▄920000 292900F29E400#29E400E290000 532A00▄545300▄552A00▄7E7F00▄7E5600▐7E5500▃7F5500▃A72A00▀7F8000▄547E00▄535400▄540000 ABAA00▄AB5500▄ABAA00▄AB5500▄530000 530000 292A00▄2A2900▄AB5500▗36D400▞553600▙AB5500▖290000 282900▄290000 280000 292800▄290000 282900▄280000 280000 292800▄280000 530000 2AFB00D2AFB00P29FB00n530000 290000 290000 290000 AC0000 A45400▄A45400▄A45400▄800000 290000 290000 290000 280000 281A00▟1AF200▗280000 280000 292800▄290000 280000 280000 280000 282900▄53FF00.FF0000 FF1800▄FF1800▄FF0000<290000 290000 2A0000 295300▀295300▀295300▀295300▀FF0000<540000 532A00▄540000 220000 002200▄000000 002200▄FF0000<920000 920000 929300▄922900▀290000 290000 7E2900▀530000 7E5400▄530000 535400▄555300▄802A00▄542A00▄7F2A00▄555400▄987F00▄7E8000▄7E5500▄7F2A00▄2A0000 7F2A00▄7F2A00▄2A0000 532A00▄530000 2A5300▄800000 805500▀800000 800000 290000 290000 290000 29D600▝D68100▄D68100▄D68100▄FF0000<290000 280000 292800▄530000 534E00▀530000 533B00▀FF0000<2A0000 290000 290000 D7AC00▀D70000 D70000 D70000 FF0000<290000 290000 290000 290000 282900▄300000 290000 FF0000<290000 290000 290000 280000 280000 290000 28FF00C28FF00a29FF00t29FF00l29FF00k290000 2A2900▄2A0000 2AFF00r29FF00p53FF00.53FF00l55FF00k2A2900▄530000 7FFF00u7FFF00a54FF00t61FF00…92FF00.92FF00l92FF00k980000 98C300▄98FF00E98FF00XC3FF00lC3FF00k980000 980000 617F00▄53FF00u55FF00r54FF00t55FF00…55FF00.AAFF00n98AA00▄7E9800▄54FF00m55FF00r7EFF00H54FF00…55FF00.54FF00n2A0000 290000 2AFF00C2AFF00m29FF00r2AFF00.2AFF00l29FF00k290000 29FF00B29FF00t28FF00t29FF00e29FF00p29FF00l29FF00n290000 28FF00328FF00P29FF00i29FF00n29FF00.29FF00n290000 290000 29FF00R29FF00y29FF00a29FF00k29FF00.29FF00n290000 290000 29FF00C2AFF00r29FF00s29FF00…29FF00.29FF00l29FF00k290000 290000 280000 290000 290000 FF0000 FF0000 290000 FF0000 FF0000 290000 2A0000 2A5300▄2AD500▁2AD500▆D50000 D50000 AB0000 290000 550000 7E0000 78FF00M78FF00i2AFF00R2AFF00l2AFF00e989300▄980000 C30000 C30000 C30000 C30000 C30000 C3C800▄C3C800▄C30000 980000 ADFF00uADFF00eAD0000 AD0000 7E0000 7F0000 800000 AA0000 2A0000 FF0000 FF0000 FF2A00▀535400▄545300▄535500▄2A0000 290000 810000 810000 810000 292A00▄290000 290000 290000 293C00▄3C0C00▄290600▄290000 290000 290000 290000 730000 B10000 FB0000 000000 290000 290000 290000 290000 FFD600▄A49E00▄734200▄484200▄D8EC00▄290000 290000 290000 00D600▀00D600▀00D600▀00D600▀00D600▀290000 290000 290000 280000 290000 290000 FF2900▀290000 290000 290000 FF2900▀290000 530000 530000 D70000 D70000 D70000 D70000 29AB00▛290000 7E0000 615400▄FF2A00IFF2A00f2A0000 2A0000 2A0000 989200▄C30000 C30000 C3A400\C8C300▄C80000 C8A400/AB0000 AB0000 C8AB00▄C80000 FF0000 FF0000 FF2A006FFAD0077F7E00▄7F8000▄547F00▄7F9800▄F60000 EE2A00▀EE2A00▀EE2A00▀545300▄532A00▄2A0000 545300▄FF0000 FF0000 FF0000 AC0000 2A0000 2A0000 290000 0C3C00▄0C0000 370700▄066600▄280000 290000 290000 290000 730000 B10000 FB0000 000000 FF0000 290000 290000 290000 7F5500▄675400▄3C3600▄0C0B00▄226900▄290000 290000 290000 000000 00F200с00F200р00F200к00F200а290000 292A00▄2A2900▄280000 290000 290000 FF0000 FF0000 290000 FF0000 FF0000<290000 530000 530000 D70000 D70000 D70000 D70000 FF0000<290000 7E5500▄540000 FF0000 FF0000 2A0000 2A0000 FF0000<920000 980000 C30000 482A00▄482A00▄FF2A00▄482A00▄AB0000 AB0000 AB0000 C80000 FF0000 FF0000 FF0000 FF0000 552A00▄807E00▄807F00▄550000 EC2A00▀EE2A00▀EE2A00▀EE2A00▀2A0000 530000 532A00▄292A00▄FF0000 FF0000 FF0000 540000 535400▄2A0000 2A0000 2A0000 2A3C00▀3C6700▄362A00▄FF0000<2A2900▄290000 290000 290000 FF0000 FF0000 FF0000 FF0000<290000 290000 290000 532900▄532900▄062800▄062800▄FF0000<290000 290000 290000 00D600▄00D600▄00D600▄00D600▄FF0000<290000 2A0000 292A00▄280000 290000 29FF00M29FF00n29FF00S2AFF00w2AFF00r2AFF00l29FF00k540000 530000 29FF00D54FF00e55FF00t53FF00.55FF00n2A0000 530000 28FF00n29FF00f29FF00P2AFF00…53FF00.53FF00l61FF00k7F5400▄98FF00o98FF00o98FF00CC3FF00…AAFF00.AAFF00nAAFF00k980000 80FF00a54FF00e53FF00d2AFF00a29FF00.61FF00n530000 2A0000 7EFF00l53FF00p54FF00y81FF00…80FF00.7FFF00n2A5300▄2AFF00H2AFF00o2AFF00o53FF00d2AFF00t2AFF00.53FF00n550000 2AFF00G2AFF00o53FF00S53FF00a53FF0022AFF00l29FF00n290000 29FF00B29FF00f29FF00e29FF00r29FF00o29FF00l29FF00k290000 29FF00P2AFF00l29FF00t2AFF00e29FF00.29FF00n290000 290000 2AFF00R2AFF00n29FF00i29FF00…2AFF00.2AFF00l2AFF00k2A0000 530000 280000 2A2900▄2A0000 2A0000 2A0000 2A0000 2A0000 2A0000 290000 540000 540000 2A0000 FD0000 FD0000 FD0000 7E0000 2A2900▄290000 280000 00FF00▄00FF00▄00FF00▄00FF00▄00FF00▀290000 2A0000 2A0000 060000 060000 060000 060000 980000 980000 7E6100▄2A5300▄2A0000 2A4800╵294800╹2A4800╹615300▄2A0000 292A00▄292A00▄555400▄2A5400▀2A5400▀7E5400▄81AA00▄7E5500▄2A5500▄292A00▄FFD800┌FF0000─FF0000─FFD800┐2A0000 542A00▄7E5500▄D50000 FFD500▀FFD500▀FFD500▀D50000 530000 540000 2A0000 292A00▄A40000 A40000 A40000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 2A0000 290000 2A2900▄290000 2A0000 2A0000 2A0000 2A0000 2A0000 2A0000 2A0000 290000 540000 540000 F70000 F7D500▄D5D600▀D5D600▀D5D600▀290000 290000 2A0000 00FF00▄00FF00▄00FF00▄00FF00▄FF0000▄2A7F00▄532800▄2A6600▄063D00C063D00l063D00e063D00tAA0000 98AA00▄617E00▄619200▄615300▄295300▄535500▄290A00▄280000 362A00▄280000 292A00▄2A0000 A44300▄A44300▄2A0000 542A00▄98AA00▄555300▄7E0000 FF0000│D80000 D80000 FF0000│545500▄2A5300▄2A0000 810000 7EFF00▒7EFF00▒55FF00▒810000 2A5400▄2A5300▄532A00▄534300▜43FF00a43FF00d43FF00i294300▛290000 2A2900▄290000 290000 290000 290000 290000 290000 290000 290000 2A0000 290000 290000 290000 290000 290000 290000 2A2900▄290000 290000 2A5300▄2A2900▄290000 2A0000 2A0000 2A0000 290000 2A0000 530000 543600▄2A0000 532A00▄7F0000 540000 7E0000 282900▄280000 280000 292800▄292800▄2A0000 295300▄2A6600▄939200▄C30000 C30000 989300▄920000 980000 C80000 C80000 C80000 939800▄979300▄C39200▄920000 920000 610000 2A5C00▄292A00▄290000 282900▄290000 282900▄292800▄545500▄532A00▄555300▄808100▄2A5400▄7F5500▄2A5500▄2A2900▄2A0000 290000 550000 540000 2A0000 2A0000 545300▄540000 540000 555400▄545500▄545300▄292A00▄290000 2A2900▄2A0000 2A0000 290000 290000 290000 290000 290000 292A00▄290000 290000 290000 290000 290000 290000 290000 290000 2A2900▄2A2900▄2A2900▄290000 290000 290000 2A0000 2A2900▄290000 290000 2A0000 2A0000 290000 2A0000 2A2900▄540000 540000 290000 7F0000 550000 7E0000 535400▄295300▄2A6100▄535500▄557F00▄7F9800▄7F9800▄7F9800▄980000 980000 980000 980000 980000 980000 AB0000 AB0000 C3AA00▄930000 930000 920000 920000 92C200▄920000 5B8D00▄305C00▄5B0000 8D0000 5B0000 2A0000 292A00▄290000 535400▄530000 555300▄987F00▄2A7E00▄555300▄535500▄292A00▄2A0000 292A00▄532900▄540000 7E5400▄530000 530000 530000 2A5300▄530000 545300▄530000 530000 2A5300▄290000 290000 2A0000 2A0000 292A00▄290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 282900▄2A0000 290000 290000 292A00▄2A0000 2A2900▄290000 2A5300▄290000 540000 545500▄2A0000 7F7E00▄557E00▄550000 556100▄530000 669200▄920000 7F9200▄989300▄930000 989300▄930000 980000 980000 939200▄939200▄989300▄AB0000 AC0000 AB0000 920000 920000 920000 920000 920000 92BD00▄920000 928D00▄928D00▄5B6100▄560000 5B0000 5B0000 290000 280000 2A2900▄545300▄2A5300▄987F00▄549800▄7E5300▄7E7F00▄2A0000 290000 2A0000 2A2900▄532A00▄540000 540000 530000 530000 2A0000 2A0000 530000 530000 530000 2A0000 292A00▄290000 290000 2A2900▄292A00▄290000 290000 290000 290000 290000 2A0000 290000 290000 290000 292800▄290000 290000 290000 290000 290000 280000 290000 290000 290000 2A2900▄532A00▄2A2900▄290000 2A0000 290000 545300▄550000 2A0000 550000 7E7F00▄550000 550000 2A0000 666100▄660000 920000 920000 920000 939200▄920000 930000 930000 920000 920000 939200▄AB0000 AC0000 ABAC00▄920000 928D00▄920000 920000 920000 920000 928D00▄8D6100▄615C00▄8D5B00▄565B00▄5B5C00▄5B0000 293000▄282900▄292800▄2A0000 530000 545300▄980000 2A0000 7F5500▄535400▄2A0000 2A5300▄290000 290000 540000 545500▄535400▄532A00▄2A5300▄2A0000 2A0000 530000 530000 2A5300▄2A0000 290000 290000 290000 2A2900▄292A00▄290000 290000 290000 2A2900▄2A2900▄2A2900▄2A2900▄292800▄290000 290000 290000 292800▄292800▄282900▄280000 290000 290000 282900▄290000 2A5300▄290000 290000 532A00▄290000 530000 550000 530000 530000 7F7E00▄7E0000 615500▄556100▄530000 610000 610000 610000 928D00▄928D00▄920000 920000 926600▄920000 618D00▄615C00▄986100▄989200▄989200▄615B00▄615C00▄610000 8D0000 8D0000 8D0000 8D0000 610000 5C0000 5C5B00▄5B0000 5B5600▄5C5B00▄300000 290000 282900▄292800▄290000 557E00▄540000 988000▄557E00▄7F7E00▄545300▄2A5300▄532A00▄530000 292A00▄532900▄550000 545500▄2A5300▄545300▄2A5300▄2A0000 2A0000 532A00▄530000 292A00▄290000 290000 292800▄290000 290000 290000 280000 280000 292800▄292800▄290000 290000 290000 290000 290000 290000 282900▄280000 280000 290000 292800▄280000 290000 290000 290000 290000 530000 292A00▄530000 550000 530000 530000 540000 540000 7E5500▄530000 610000 530000 615300▄610000 920000 920000 920000 920000 920000 615300▄2A5300▄292A00▄2A2900▄2A5C00▄532900▄2A2900▄2A2900▄610000 8D0000 8D0000 8D0000 610000 5C6100▄5B5C00▄2A0000 300000 2B0000 302900▄290000 290000 290000 290000 290000 532A00▄547F00▄7F0000 7E0000 535400▄7E5500▄530000 530000 2A5300▄532A00▄292A00▄290000 532900▄540000 530000 535400▄530000 530000 2A0000 2A0000 530000 530000 292A00▄290000 290000 280000 290000 290000 292800▄280000 292800▄290000 290000 290000 290000 290000 290000 290000 292800▄280000 290000 280000 280000 292800▄290000 290000 290000 532A00▄2A0000 530000 530000 2A0000 530000 2A0000 550000 7F0000 530000 2A0000 532A00▄2A5300▄615C00▄610000 610000 8D6100▄920000 929300▄930000 7F9800▄617E00▄536100▄2A0000 2A5300▄610000 8D0000 8D0000 8D0000 8D0000 610000 29D600▀29FF00▀29FF00▀29D600▀300000 300000 290000 290000 544800S540000 540000 540000 282900▄555400▄545500▄555400▄54D700▒2AD700▒7FD700▒55D700▐557E00▄292A00▄532A00▄2A5300▄292A00▄290000 2A0000 530000 555400▄545500▄530000 530000 2A0000 292A00▄2A0000 2A0000 290000 290000 292800▄290000 290000 290000 282900▄290000 280000 282900▄280000 290000 290000 290000 290000 280000 290000 290000 280000 280000 290000 290000 290000 2A0000 2A0000 2A0000 545300▄290000 2A5300▄540000 540000 550000 557E00▄2A5300▄2A2900▄530000 532A00▄5C0000 5C0000 615C00▄610000 926600▄932A00▄8D3000▄5B3000▄8D0000 55D500╚55D500╬55D500╬55D500╝935400▄987F00▄7F0000 7F5500▄295500-295500c295500o295500m545500▄550000 540000 540000 FF0000 FF0000 FF0000 FF0000 540000 540000 7F0000 7F0000 7FAC00▜7EAC00▒55AC00▒7F7E00▄818000▄7E7F00▄295300▄2A2900▄530000 2A5300▄290000 290000 532A00▄530000 555300▄555400▄530000 530000 2A0000 290000 290000 290000 290000 282900▄292800▄290000 280000 290000 290000 282900▄290000 290000 290000 290000 290000 280000 290000 290000 282900▄280000 290000 290000 290000 2A0000 290000 2A0000 540000 2A2900▄530000 540000 535400▄2A0000 7E0000 535400▄530000 290000 530000 530000 530000 535C00▄296100▄285C00▄295C00▄2A5B00▄930000 939800▄93C900▄93AA00▄93C400▄930000 7F0000 7F9800▄7F9800▄980000 980000 980000 800000 7F0000 7F0000 7F0000 7E0000 7F0000 7F0000 7F0000 7F0000 7F0000 7F0000 7F0000 7F0000 818000▄808100▄810000 7F8000▄7F0000 7F0000 818000▄7F0000 2A5400▄292A00▄532A00▄530000 290000 290000 290000 530000 545300▄540000 540000 2A5300▄2A0000 292A00▄290000 290000 290000 290000 280000 292800▄280000 292800▄290000 290000 290000 290000 290000 290000 ]], - downloading = [[3F100000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0B0000 0B0000 410000 410000 0B0000 0B0000 410000 410000 0B0000 0B0000 500000 500000 500000 500000 500000 500000 0B0000 0B0000 410000 410000 0B0000 0B0000 3B0000 3B0000 3B0000 3B0000 0B0000 0B0000 410000 410000 0B0000 0B0000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0B0000 0B0000 410000 410000 0B0000 0B0000 410000 410000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 410000 410000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 410000 410000 0B0000 0B0000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 180000 0018FF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0B0000 0B0000 410000 410000 0B0000 0B0000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 0B0000 0B0000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 180000 180000 180000 0018FF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0B0000 0B0000 410000 410000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 180000 180000 180000 180000 180000 0018FF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0B0000 0B0000 410000 410000 0B0000 0B0000 410000 410000 410000 410000 0B0000 0B0000 290000 290000 290000 290000 0B0000 0B0000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 0B0000 0B0000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 0018FF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0B0000 0B0000 410000 410000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 290000 290000 290000 290000 0B0000 0B0000 410000 410000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0B0000 0B0000 410000 410000 0B0000 0B0000 410000 410000 410000 410000 0B0000 0B0000 290000 290000 290000 290000 0B0000 0B0000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 0B0000 0B0000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 180000 0018FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0B0000 0B0000 410000 410000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 290000 290000 290000 290000 0B0000 0B0000 410000 410000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 180000 180000 180000 180000 180000 0018FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0B0000 0B0000 410000 410000 0B0000 0B0000 410000 410000 410000 410000 0B0000 0B0000 290000 290000 290000 290000 0B0000 0B0000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 0B0000 0B0000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 180000 180000 180000 0018FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0B0000 0B0000 410000 410000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 290000 290000 290000 290000 0B0000 0B0000 410000 410000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 180000 0018FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0B0000 0B0000 410000 410000 0B0000 0B0000 410000 410000 410000 410000 0B0000 0B0000 290000 290000 290000 290000 0B0000 0B0000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 0B0000 0B0000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0B0000 0B0000 410000 410000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 290000 290000 290000 290000 0B0000 0B0000 410000 410000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0B0000 0B0000 410000 410000 0B0000 0B0000 410000 410000 410000 410000 0B0000 0B0000 290000 290000 290000 290000 0B0000 0B0000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 410000 0B0000 0B0000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0B0000 0B0000 410000 410000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 290000 290000 290000 290000 0B0000 0B0000 410000 410000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 0B0000 ]], - OK = [[24120000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF DDE700▄DDE200▄DDE200▄DDE200▄D8E200▄DDDD00▄D8DD00▄D8DD00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 00FBFF▄ECF600▄ECF100▄ECF100▄ECEC00▄E7F100▄E7EC00▄E7EC00▄E7E7FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 00FBFF▄FBFB00▄F6FB00▄FBFB00▄F6FB00▄F6FB00▄F6FB00▄F6FB00▄F6F6FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF A5A500▄A5A500▄D0A500▄D0A500▄D0A500▄D0D000▄D0D000▄FBD100▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 794E00▄794E00▄794E00▄794E00▄A54E00▄A54E00▄797900▄A57900▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 232300▄232300▄232300▄4E2300▄232300▄4E2300▄4E2300▄4E2300▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0025FF▄242500▄242400▄242400▄242400▄242400▄232400▄242400▄7B7BFF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0026FF▄252600▄252600▄262600▄252600▄252600▄252500▄255100▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 1818FF▀1D1300▄1D1300▄1D1300▄1D1800▄1D1800▄221800▄221800▄001DFF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF A92700▄272200▄272700▄272700▄272700▄262700▄272700▄2626FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0E0EFF▀0E0900▄0E0900▄0E0900▄0E0900▄130E00▄130900▄130E00▄130E00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 4D1800▄1D1800▄221800▄221800▄221D00▄221D00▄221D00▄2222FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 043400▄042F00▄090400▄040400▄090400▄090400▄090400▄090400▄0039FF▄0000FF 0000FF 0000FF 0000FF 0E0900▄0E0900▄130900▄130E00▄130E00▄130E00▄180E00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 2F2FFF▀2F5A00▄2F5A00▄2F2F00▄2F2F00▄042F00▄2F2F00▄042F00▄002FFF▄0000FF 0004FF▄040400▄042F00▄040400▄040400▄090400▄040400▄090400▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 5A8600▄868600▄5AB100▄5A8600▄5A8600▄5A8600▄5A5A00▄5A8600▄2F5A00▄5A5A00▄2F5A00▄2F5A00▄2F5A00▄2F2F00▄2F2FFF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF B1B100▄B1B100▄B1DC00▄B1B100▄86DC00▄B1B100▄86B100▄86B100▄86B100▄86B100▄86B100▄868600▄8686FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF DCDCFF▀DCDB00▄DCDB00▄DCDC00▄DCDB00▄DCDC00▄DCDC00▄B1DC00▄B1DC00▄B1DC00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF DBDBFF▀DBDA00▄DBDA00▄DBDA00▄DBDB00▄DBDA00▄DBDB00▄DBDBFF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF D9D9FF▀D9D900▄D9D900▄DAD800▄DADAFF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF D8D8FF▀D8D800▄D8D8FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF ]], - EEPROM = [[1E120000FF 0000FF 0000FF 0000FF 0000FF 0000FF 6C4100▄6C6B00▄416C00▄3B5500▄3B0000 553B00▄3B3C00▄3B5500▄3B0000 553B00▄3B3C00▄3B5500▄3B0000 553B00▄3B3C00▄363B00▄360000 363B00▄3B3600▄360000 363B00▄363B00▄360000 3B3600▄0000FF 0000FF 0000FF 0041FF▄006CFF▄006CFF▄6C3B00▄6C3B00▄413B00▄3C6C00▄3B6C00▄554600▄3B6C00▄3B6C00▄3C6C00▄554600▄3B6C00▄3B6C00▄3C7E00▄3B6C00▄3B6C00▄364100▄366C00▄364100▄3B7E00▄363C00▄364000▄360B00▄3B2A00▄362A00▄0000FF 0000FF 0000FF 6C4100▄6B6C00▄416C00▄3B0000 554100▄3B0000 716C00▄6C4100▄6C7100▄6C0000 466B00▄6C4100▄6C7100▄6C0000 466B00▄416C00▄404100▄7E6C00▄404100▄6C6B00▄406C00▄403C00▄7E4000▄417E00▄2A0000 2A0000 0B2A00▄3B0000 3B3C00▄553B00▄3B0000 3B7E00▄3B0000 416C00▄7E4000▄6C4100▄407E00▄7E4000▄404100▄3B7E00▄410000 6B5500▄CD0000 CD0000 CD0000 3B7E00▄3B4000▄3B5500▄C8CD00▄CDC800▄C8CD00▄404100▄7E0000 404100▄0B2A00▄0A2A00▄2A0000 553600▄3B0000 553600▄3C7100▄3B7100▄3C7100▄6B6C00▄417E00▄7E4100▄40CD00▄67D200▄55CD00▄406C00▄7E4100▄407E00▄CD0000 F8C800▄CD0000 3B5500▄3C3B00▄3B7E00▄CD9D00▄CDC800▄CD9D00▄667E00▄404100▄554000▄2A0000 0B2A00▄2A0B00▄360000 363B00▄3B3600▄9D7200▄729D00▄817100▄410000 407E00▄6C4100▄CDF800▄F9CD00▄CD0000 556C00▄404100▄3B6700▄CD0000 CDC800▄CD0000 3B7E00▄415500▄3B0000 C7C800▄9D0000 C7C800▄3C4000▄407E00▄7E4100▄0B2A00▄2A0A00▄2A0000 2A0000 0B2A00▄2A0000 3B0000 3B3C00▄553B00▄6C4100▄407E00▄6C4100▄CD0000 CDC800▄CEF800▄3B4100▄7E4000▄406700▄7E4100▄407E00▄7E4100▄3B0000 405500▄3C3B00▄3B7E00▄554000▄3B3C00▄7E4000▄415500▄664100▄052900▄292800▄290000 002AFF▀002AFF▀000BFF▀550500▄3B2900▄3B2900▄6C3B00▄403B00▄7E5500▄CE3B00▄CD3B00▄CD5500▄554000▄673B00▄403B00▄3B3C00▄405500▄6C3B00▄3B0000 3C3B00▄405400▄3C3B00▄3B0000 665400▄414000▄405500▄7E4100▄052900▄290500▄282900▄0000FF 0000FF 0000FF 292800▄290500▄290000 3C4000▄7E3B00▄405500▄3C3B00▄553B00▄3B3C00▄3C3B00▄553B00▄3B3C00▄554000▄3B0000 3C3B00▄543B00▄3B5500▄3B0000 543B00▄3B5400▄3C3B00▄7E4000▄417E00▄405500▄290500▄290000 052900▄360000 363B00▄3B3600▄3B0000 3C5500▄3B0000 7E3B00▄3B3C00▄3C4000▄405500▄553B00▄3B3C00▄3B6600▄553C00▄3B0000 553B00▄3C3B00▄553B00▄3B0000 3B3700▄363B00▄3C3B00▄3B3600▄543B00▄3C6B00▄404100▄417E00▄292800▄290500▄282900▄363B00▄360000 3B3600▄3C7100▄408100▄3C7100▄7E4000▄3B3C00▄554000▄3BAB00▄3CAB00▄3BAB00▄3BAB00▄3BAB00▄55AB00▄3CCE00▄55AC00▄3BAC00▄55AC00▄3BAC00▄3BAC00▄549E00▄3BAA00▄3CAA00▄3C4000▄663C00▄407E00▄290500▄290000 282900▄360000 3B3600▄360000 9D7200▄729D00▄710000 550000 3B4100▄553B00▄AB0000 AB0000 AB0000 AB0000 AB0000 AB0000 ABAC00▄AC0000 AC0000 ABAC00▄AC0000 AC0000 ABAA00▄AB0000 ABAA00▄554100▄6C4000▄403C00▄062900▄052900▄282900▄2A0000 0B2A00▄2A0B00▄407E00▄414000▄6C3C00▄3B7E00▄3B4000▄7E3B00▄D50000 D50000 D50000 FF0000 FF0000 FF0000 FF0000 FF0000 FF0000 D60000 D6D700▄D60000 D50000 D50000 D50000 363B00▄360000 3B3600▄282900▄280500▄290000 0B2A00▄2A3500▄2A0000 413B00▄7E3B00▄6C3B00▄3B0000 553B00▄3C3B00▄D5AA00▄ACAA00▄D5AA00▄FFAC00▄FFD500▄FFAC00▄FFAB00▄FFAB00▄FFAB00▄D6AC00▄D6AB00▄D7AB00▄D5AB00▄D5AB00▄D5AB00▄3B2A00▄360000 3B3600▄290000 290500▄282900▄2A0000 0B2A00▄2A0B00▄3B0000 3B5500▄3B0000 3C5500▄3B0000 553B00▄AA0000 AA8100▄AA0000 AC0000 ACD500▄AC0000 ACAB00▄ABAC00▄AB0000 ABAC00▄AB0000 AB0000 AB0000 AAAB00▄ABAA00▄360000 362A00▄0B3600▄280500▄290000 052900▄052900▄282900▄292800▄3B5500▄3C3B00▄3B0000 360000 360000 360000 0B3600▄362A00▄2A3600▄360B00▄2A3600▄360B00▄362A00▄2A0B00▄362A00▄2A0B00▄362A00▄2A0B00▄2A0000 2A0B00▄2A0000 360B00▄0B3500▄360000 282900▄290500▄282900▄282900▄052900▄290000 3B2900▄3B2900▄3C2800▄360500▄362900▄3B0500▄2A2900▄0B0500▄2A2900▄2A0500▄352900▄360500▄2A0500▄360500▄360500▄2A0500▄2A0500▄2A0500▄362800▄350600▄2A0500▄360500▄362900▄0B2900▄052900▄290500▄290000 052900▄292800▄052900▄290000 052800▄290000 290500▄290000 280500▄290000 290500▄052900▄290000 292800▄290000 290500▄290000 292800▄290000 290500▄290000 052900▄290000 290500▄282900▄292800▄052900▄282900▄290500▄282900▄]], -} - -for key in pairs(images) do - images[key] = image.fromString(images[key]) - os.sleep(0.05) -end - ------------------------------------------------------------------------------------------------------------------------------------- - -local web = require("web") -local buffer = require("doubleBuffering") -local GUI = require("GUI") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") - -buffer.setResolution(gpu.maxResolution()) -local application = GUI.application() -application:addChild(GUI.panel(1, 1, application.width, application.height, 0x2D2D2D)) - -local stageContainer = application:addChild(GUI.container(math.floor(application.width / 2 - 90 / 2), math.floor(application.height / 2 - 28 / 2), 90, 28)) -stageContainer:addChild(GUI.panel(1, 1, stageContainer.width, stageContainer.height, 0xDDDDDD)) - -local overrideDraw = stageContainer.draw -stageContainer.draw = function(...) - overrideDraw(...) - GUI.drawShadow(stageContainer.x, stageContainer.y, stageContainer.width, stageContainer.height, 0.5, true) -end - ------------------------------------------------------------------------------------------------------------------------------------- - -local stages = {current = 1} -local localization - -local function addButtonsToStage() - local buttonWidth = 7 - local spaceBetween = 5 - - local totalWidth = (stages.current > 1 and buttonWidth or 0) + (stages.current > 1 and stages.current < #stages and spaceBetween or 0) + (stages.current < #stages and buttonWidth or 0) - local x = math.floor(stageContainer.width / 2 - totalWidth / 2) + 1 - local y = stageContainer.height - 3 - - if stages.current > 1 then - stageContainer.previousStageButton = stageContainer:addChild(GUI.roundedButton(x, y, buttonWidth, 3, 0xAAAAAA, 0xDDDDDD, 0x777777, 0xDDDDDD, "⇦")) - stageContainer.previousStageButton.colors.disabled.background = 0xCCCCCC - stageContainer.previousStageButton.colors.disabled.text = 0xDDDDDD - stageContainer.previousStageButton.onTouch = function() - stages.load(stages.current - 1) - end - x = x + stageContainer.previousStageButton.width + spaceBetween - end - - if stages.current < #stages then - stageContainer.nextStageButton = stageContainer:addChild(GUI.roundedButton(x, y, buttonWidth, 3, 0xAAAAAA, 0xDDDDDD, 0x777777, 0xDDDDDD, "⇨")) - stageContainer.nextStageButton.colors.disabled.background = 0xCCCCCC - stageContainer.nextStageButton.colors.disabled.text = 0xDDDDDD - stageContainer.nextStageButton.onTouch = function() - stages.load(stages.current + 1) - end - end -end - -function stages.load(stage) - stages.current = stage - stageContainer:removeChildren(2) - - stages[stage]() - - application:draw() -end - -local function addImageToStage(y, picture) - stageContainer:addChild(GUI.image(math.floor(stageContainer.width / 2 - image.getWidth(picture) / 2), y, picture)) - return y + image.getHeight(picture) - 1 -end - ------------------------------------------------------------------------------------------------------------------------------------- - -stages[1] = function() - addButtonsToStage() - local y = addImageToStage(3, images.languages) - y = y + 3 - local comboBox = stageContainer:addChild(GUI.comboBox(math.floor(stageContainer.width / 2 - 15), y, 30, 3, 0xFFFFFF, 0x555555, 0xAAAAAA, 0xDDDDDD)) - - for i = 1, #applicationList.localizations do - local name = fs.hideExtension(fs.name(applicationList.localizations[i].path)) - comboBox:addItem(name).onTouch = function() - MineOSCore.properties.language = name - localization = table.fromString(web.request(URLs.installerLocalization .. name .. ".lang")) - end - end - - comboBox:getItem(1).onTouch() -end - ------------------------------------------------------------------------------------------------------------------------------------- - -local stageSwitchWidth = 8 - -local function addSwitchToStage(x, y, color, text, state) - stageContainer:addChild(GUI.text(math.floor(x + stageSwitchWidth / 2 - unicode.len(text) / 2), y + 2, 0x555555, text)) - return stageContainer:addChild(GUI.switch(x, y, stageSwitchWidth, color, 0xAAAAAA, 0xFFFFFF, state)) -end - -stages[2] = function() - addButtonsToStage() - stageContainer:addChild(GUI.image(1, 1, images.OS)) - - local spacing = math.max(unicode.len(localization.installSomeApps), unicode.len(localization.installWallpapers), unicode.len(localization.flashEEPROM)) + 2 - - stageContainer.installSomeAppsSwitch = addSwitchToStage(math.floor(stageContainer.width / 2 - spacing - stageSwitchWidth / 2), 22, 0xFF4940, localization.installSomeApps, true) - stageContainer.downloadWallpapersSwitch = addSwitchToStage(math.floor(stageContainer.width / 2 - stageSwitchWidth / 2), 22, 0x3392FF, localization.installWallpapers, true) - stageContainer.flashEEPROMSwitch = addSwitchToStage(math.floor(stageContainer.width / 2 + spacing - stageSwitchWidth / 2), 22, 0x33FF80, localization.flashEEPROM, true) -end - ------------------------------------------------------------------------------------------------------------------------------------- - -stages[3] = function() - addButtonsToStage() - stageContainer:addChild(GUI.textBox(1, 1, 90, 20, 0xFFFFFF, 0x444444, string.wrap(web.request(URLs.license), 88), 1, 1, 1)) - - stageContainer.nextStageButton.disabled = true - local switch = addSwitchToStage(41, 22, 0x666666, localization.terms, false) - switch.onStateChanged = function() - stageContainer.nextStageButton.disabled = not switch.state - application:draw() - end -end - ------------------------------------------------------------------------------------------------------------------------------------- - -stages[4] = function() - local y = addImageToStage(5, images.downloading) - y = y + 3 - stageContainer.nextStageButton.disabled, stageContainer.previousStageButton.disabled = true, true - - local width = 62 - local x = math.floor(stageContainer.width / 2 - width / 2) - local progressBar = stageContainer:addChild(GUI.progressBar(x, y, width, 0x3392FF, 0xCCCCCC, 0x555555, 0, true, false)) - local fileLabel = stageContainer:addChild(GUI.label(x, y + 1, width, 1, 0x666666, "")) - - if stageContainer.downloadWallpapersSwitch.state then - for i = 1, #applicationList.wallpapers do - table.insert(applicationList.duringInstall, applicationList.wallpapers[i]) - applicationList.wallpapers[i] = nil - end - end - - for i = 1, #applicationList.localizations do - table.insert(applicationList.duringInstall, applicationList.localizations[i]) - applicationList.localizations[i] = nil - end - - if stageContainer.installSomeAppsSwitch.state then - for i = 1, #applicationList.optional do - table.insert(applicationList.duringInstall, applicationList.optional[i]) - applicationList.optional[i] = nil - end - end - - for i = 1, #applicationList.duringInstall do - fileLabel.text = localization.downloading .. " " .. string.limit(applicationList.duringInstall[i].path, width - unicode.len(localization.downloading) - 1, "left") - progressBar.value = math.round(i / #applicationList.duringInstall * 100) - - application:draw() - - web.download(applicationList.duringInstall[i].url, applicationList.duringInstall[i].path) - - if applicationList.duringInstall[i].createShortcut then - local path = fs.path(applicationList.duringInstall[i].path) - MineOSCore.createShortcut(MineOSPaths.desktop .. fs.hideExtension(fs.name(path)) .. ".lnk", path) - end - - storeFileVersion(applicationList.duringInstall[i]) - end - - if stageContainer.flashEEPROMSwitch.state then - stageContainer:removeChildren(2) - y = addImageToStage(4, images.EEPROM) - stageContainer:addChild(GUI.label(1, y + 3, stageContainer.width, 1, 0x666666, localization.flashingEEPROM)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) - application:draw() - - component.eeprom.set(web.request(URLs.EFI)) - end - - stages.load(5) -end - ------------------------------------------------------------------------------------------------------------------------------------- - -stages[5] = function() - addImageToStage(3, images.OK) - stageContainer.children[#stageContainer.children].localX = stageContainer.children[#stageContainer.children].localX + 3 - - stageContainer:addChild(GUI.label(1, 22, stageContainer.width, 1, 0x666666, localization.needToReboot)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) - stageContainer:addChild(GUI.adaptiveRoundedButton(math.floor(stageContainer.width / 2 - (unicode.len(localization.reboot) + 4) / 2), stageContainer.height - 4, 2, 1, 0xAAAAAA, 0xDDDDDD, 0x777777, 0xDDDDDD, localization.reboot)).onTouch = function() - table.toFile(MineOSPaths.fileVersions, fileVersions) - - MineOSCore.properties.wallpaperEnabled = stageContainer.downloadWallpapersSwitch.state - MineOSCore.saveProperties() - - local file = io.open("/autorun.lua", "w") - file:write("require(\"shell\").execute(\"OS\")") - file:close() - - -- if computer.getArchitecture then - -- computer.setArchitecture("Lua 5.2") - -- end - - computer.shutdown(true) - end -end - ------------------------------------------------------------------------------------------------------------------------------------- - -stages.load(1) -application:draw(true) -application:start() diff --git a/Installer/Files.cfg b/Installer/Files.cfg new file mode 100644 index 00000000..9ea3917b --- /dev/null +++ b/Installer/Files.cfg @@ -0,0 +1,229 @@ +{ + installerFiles = { + "Libraries/Event.lua", + "Libraries/Keyboard.lua", + "Libraries/Filesystem.lua", + "Libraries/Color.lua", + "Libraries/Image.lua", + "Libraries/Screen.lua", + "Libraries/Text.lua", + "Libraries/Number.lua", + "Libraries/Paths.lua", + "Libraries/GUI.lua", + "Libraries/System.lua", + "Libraries/SHA-256.lua", + "Installer/Pictures/Languages.pic", + "Installer/Pictures/HDD.pic", + "Installer/Pictures/User.pic", + "Installer/Pictures/Settings.pic", + "Installer/Pictures/Downloading.pic", + "Installer/Pictures/EEPROM.pic", + "Installer/Pictures/Done.pic", + }, + localizations = { + "Localizations/English.lang", + "Localizations/Russian.lang", + "Localizations/French.lang", + "Localizations/Ukrainian.lang", + }, + required = { + -- Libraries + { path="Libraries/Bit32.lua" }, + { path="Libraries/Component.lua" }, + { path="Libraries/Event.lua" }, + { path="Libraries/Keyboard.lua" }, + { path="Libraries/Filesystem.lua" }, + { path="Libraries/Color.lua" }, + { path="Libraries/Image.lua" }, + { path="Libraries/Screen.lua" }, + { path="Libraries/Paths.lua" }, + { path="Libraries/Text.lua" }, + { path="Libraries/Number.lua" }, + { path="Libraries/GUI.lua" }, + { path="Libraries/System.lua" }, + { path="Libraries/Network.lua" }, + { path="Libraries/Archive.lua" }, + { path="Libraries/SHA-256.lua" }, + { path="Libraries/Internet.lua" }, + -- Scripts + { path="OS.lua", id=106 }, + -- Extensions + "Extensions/Lua/ContextMenu.lua", + "Extensions/Lua/Launcher.lua", + "Extensions/Pic/ContextMenu.lua", + "Extensions/Arc/Launcher.lua", + "Extensions/3dm/ContextMenu.lua", + -- System icons + "Icons/3DModel.pic", + "Icons/Application.pic", + "Icons/Archive.pic", + "Icons/Config.pic", + "Icons/FileNotExists.pic", + "Icons/Floppy.pic", + "Icons/Folder.pic", + "Icons/HDD.pic", + "Icons/Image.pic", + "Icons/Lua.pic", + "Icons/Script.pic", + "Icons/Text.pic", + "Icons/Trash.pic", + "Icons/User.pic", + -- Sample + "Applications/Sample.app/Main.lua", + "Applications/Sample.app/Icon.pic", + "Applications/Sample.app/Localizations/Russian.lang", + "Applications/Sample.app/Localizations/English.lang", + "Applications/Sample.app/Localizations/French.lang", + "Applications/Sample.app/Localizations/Ukrainian.lang", + -- App market + { path="Applications/App Market.app/Main.lua", id=164 }, + "Applications/App Market.app/Icon.pic", + "Applications/App Market.app/Localizations/Russian.lang", + "Applications/App Market.app/Localizations/English.lang", + "Applications/App Market.app/Localizations/French.lang", + "Applications/App Market.app/Localizations/Ukrainian.lang", + -- MineCode IDE + { path="Applications/MineCode IDE.app/Main.lua", id=169 }, + "Applications/MineCode IDE.app/Icon.pic", + "Applications/MineCode IDE.app/Localizations/Russian.lang", + "Applications/MineCode IDE.app/Localizations/English.lang", + "Applications/MineCode IDE.app/Localizations/French.lang", + "Applications/MineCode IDE.app/Localizations/Ukrainian.lang", + -- Picture Edit + { path="Applications/Picture Edit.app/Main.lua", id=798 }, + "Applications/Picture Edit.app/Icon.pic", + "Applications/Picture Edit.app/Tools/1.lua", + "Applications/Picture Edit.app/Tools/2.lua", + "Applications/Picture Edit.app/Tools/3.lua", + "Applications/Picture Edit.app/Tools/4.lua", + "Applications/Picture Edit.app/Tools/5.lua", + "Applications/Picture Edit.app/Tools/6.lua", + "Applications/Picture Edit.app/Tools/7.lua", + "Applications/Picture Edit.app/Tools/8.lua", + -- Finder + { path="Applications/Finder.app/Main.lua", id=175 }, + "Applications/Finder.app/Icon.pic", + -- Settings + { path="Applications/Settings.app/Main.lua", id=909 }, + "Applications/Settings.app/Icon.pic", + "Applications/Settings.app/Localizations/English.lang", + "Applications/Settings.app/Localizations/Russian.lang", + "Applications/Settings.app/Localizations/French.lang", + "Applications/Settings.app/Localizations/Ukrainian.lang", + "Applications/Settings.app/Modules/0_Screen/Main.lua", + "Applications/Settings.app/Modules/0_Screen/Icon.pic", + "Applications/Settings.app/Modules/1_Wallpaper/Main.lua", + "Applications/Settings.app/Modules/1_Wallpaper/Icon.pic", + "Applications/Settings.app/Modules/2_Icons/Main.lua", + "Applications/Settings.app/Modules/2_Icons/Icon.pic", + "Applications/Settings.app/Modules/3_Tasks/Main.lua", + "Applications/Settings.app/Modules/3_Tasks/Icon.pic", + "Applications/Settings.app/Modules/40_Users/Main.lua", + "Applications/Settings.app/Modules/40_Users/Icon.pic", + "Applications/Settings.app/Modules/4_Disks/Main.lua", + "Applications/Settings.app/Modules/4_Disks/Icon.pic", + "Applications/Settings.app/Modules/5_Network/Main.lua", + "Applications/Settings.app/Modules/5_Network/Icon.pic", + "Applications/Settings.app/Modules/6_Localizations/Main.lua", + "Applications/Settings.app/Modules/6_Localizations/Icon.pic", + "Applications/Settings.app/Modules/7_Time/Main.lua", + "Applications/Settings.app/Modules/7_Time/Icon.pic", + "Applications/Settings.app/Modules/8_System/Main.lua", + "Applications/Settings.app/Modules/8_System/Icon.pic", + }, + optional = { + { path="Libraries/JSON.lua", id=103 }, + { path="Libraries/BigLetters.lua", id=391 }, + { path="Libraries/MeowEngine/Main.lua", id=340 }, + { path="Libraries/OpenComputersGL/Main.lua", id=337 }, + { path="Libraries/OpenComputersGL/Renderer.lua", id=338 }, + { path="Libraries/OpenComputersGL/Materials.lua", id=339 }, + { path="Libraries/Vector.lua", id=520 }, + -- 3D Print + { path="Applications/3D Print.app/Main.lua", id=859, shortcut = true }, + "Applications/3D Print.app/Icon.pic", + "Applications/3D Print.app/Localizations/English.lang", + "Applications/3D Print.app/Localizations/Russian.lang", + "Applications/3D Print.app/Localizations/French.lang", + "Applications/3D Print.app/Localizations/Ukrainian.lang", + -- Weather + { path="Applications/Weather.app/Main.lua", id=240, shortcut = true }, + "Applications/Weather.app/Icon.pic", + "Applications/Weather.app/Sunny.pic", + "Applications/Weather.app/Cloudy.pic", + "Applications/Weather.app/Rainy.pic", + "Applications/Weather.app/Foggy.pic", + "Applications/Weather.app/Snowy.pic", + "Applications/Weather.app/Stormy.pic", + -- Calculator + { path="Applications/Calculator.app/Main.lua", id=1045, shortcut = true }, + "Applications/Calculator.app/Icon.pic", + -- Christmas tree + { path="Applications/Christmas Tree.app/Main.lua", shortcut = true }, + "Applications/Christmas Tree.app/Icon.pic", + -- Fuck the rain + { path="Applications/Fuck the Rain.app/Main.lua", id=506, shortcut = true }, + "Applications/Fuck the Rain.app/Icon.pic", + -- OS Installer + { path="Applications/Reinstall OS.app/Main.lua", shortcut = true }, + "Applications/Reinstall OS.app/Icon.pic", + -- Graph + { path="Applications/Graph.app/Main.lua", id=238, shortcut = true }, + "Applications/Graph.app/Icon.pic", + -- HEX + { path="Applications/HEX.app/Main.lua", id=248, shortcut = true }, + "Applications/HEX.app/Icon.pic", + -- Running string + { path="Applications/Running String.app/Main.lua", id=410, shortcut = true }, + "Applications/Running String.app/Icon.pic", + -- Palette + { path="Applications/Palette.app/Main.lua", id=250, shortcut = true }, + "Applications/Palette.app/Icon.pic", + -- HoloClock + { path="Applications/HoloClock.app/Main.lua", id=559, shortcut = true }, + "Applications/HoloClock.app/Icon.pic", + -- Symbols + { path="Applications/Symbols.app/Main.lua", id=853, shortcut = true }, + "Applications/Symbols.app/Icon.pic", + -- IRC + { path="Applications/IRC.app/Main.lua", id=856, shortcut = true }, + "Applications/IRC.app/Icon.pic", + -- Nanomachines + { path="Applications/Nanomachines.app/Main.lua", id=938, shortcut = true }, + "Applications/Nanomachines.app/Icon.pic", + "Applications/Nanomachines.app/Localizations/English.lang", + "Applications/Nanomachines.app/Localizations/Russian.lang", + "Applications/Nanomachines.app/Localizations/French.lang", + "Applications/Nanomachines.app/Localizations/Ukrainian.lang", + -- VK + { path="Applications/VK.app/Main.lua", id=952, shortcut = true }, + "Applications/VK.app/Icon.pic", + "Applications/VK.app/Localizations/English.lang", + "Applications/VK.app/Localizations/Russian.lang", + "Applications/VK.app/Icons/Likes.pic", + "Applications/VK.app/Icons/Reposts.pic", + "Applications/VK.app/Icons/Comments.pic", + "Applications/VK.app/Icons/Views.pic", + "Applications/VK.app/Styles/Bright.lua", + "Applications/VK.app/Styles/Dark.lua", + }, + wallpapers = { + "Pictures/AhsokaTano.pic", + "Pictures/Block.pic", + "Pictures/Ciri.pic", + "Pictures/Girl.pic", + "Pictures/Mystery.pic", + "Pictures/Nettle.pic", + "Pictures/Raspberry.pic", + "Pictures/Road.pic", + "Pictures/Space.pic", + }, + screensavers = { + "Screensavers/Matrix.lua", + "Screensavers/Mandala.lua", + "Screensavers/Clock.lua", + "Screensavers/Lines.lua", + "Screensavers/XCOM.lua", + "Screensavers/NyanCat.lua", + } +} \ No newline at end of file diff --git a/Installer/Localizations/English.lang b/Installer/Localizations/English.lang new file mode 100644 index 00000000..d3e7c5b2 --- /dev/null +++ b/Installer/Localizations/English.lang @@ -0,0 +1,22 @@ +{ + username = "User name", + password = "Password", + submitPassword = "Submit password", + passwordsArentEqual = "Passwords aren't equal", + withoutPassword = "Without password:", + wallpapers = "Wallpapers:", + screensavers = "Screensavers:", + applications = "Third party apps:", + languages = "Language packs:", + accept = "I accept this shit:, + select = "Select filesystem for installation", + erase = "Erase", + used = "used", + setup = "Setup your profile", + customize = "Customize downloads", + flashing = "Flashing EEPROM...", + creating = "Creating user profile...", + installing = "Installing", + installed = "MineOS has been installed. Enjoy!", + reboot = "Reboot", +} diff --git a/Installer/Localizations/French.lang b/Installer/Localizations/French.lang new file mode 100644 index 00000000..5bd7551b --- /dev/null +++ b/Installer/Localizations/French.lang @@ -0,0 +1,22 @@ +{ + username = "Nom d'utilisateur", + password = "Mot de passe", + submitPassword = "Soumettre mot de passe", + passwordsArentEqual = "Les mots de passe ne sont pas égaux", + withoutPassword = "Sans mot de passe:", + wallpapers = "Peint:", + screensavers = "Économiseur:", + applications = "Les applications tierces:", + languages = "Des packs de langue:", + accept = "J'accepte cette merde:", + select = "Sélectionnez système de fichiers pour l'installation", + erase = "Effacer", + used = "utiliser", + setup = "Configurer votre profil", + customize = "Personnaliser les téléchargements", + flashing = "EEPROM clignotant...", + creating = "La création d'un profil d'utilisateur...", + installing = "Installer", + installed = "Des Mines ont été installées. Profitez-en!", + reboot = "Redémarrer", +} diff --git a/Installer/Localizations/Russian.lang b/Installer/Localizations/Russian.lang new file mode 100644 index 00000000..83922af1 --- /dev/null +++ b/Installer/Localizations/Russian.lang @@ -0,0 +1,22 @@ +{ + username = "Имя пользователя", + password = "Пароль", + submitPassword = "Подтвердите пароль", + passwordsArentEqual = "Пароли не совпадают", + withoutPassword = "Без пароля:", + wallpapers = "Обои:", + screensavers = "Заставки:", + applications = "Приложения:", + languages = "Языковые пакеты:", + accept = "Условия принимаю:", + select = "Выберите файловую систему для установки", + erase = "Стереть", + used = "исп.", + setup = "Профиль пользователя", + customize = "Настройка загрузок", + flashing = "Прошивка EEPROM...", + creating = "Создание профиля пользователя...", + installing = "Установка", + installed = "MineOS установлена. Наслаждайтесь!", + reboot = "Перезагрузить", +} diff --git a/Installer/Localizations/Ukrainian.lang b/Installer/Localizations/Ukrainian.lang new file mode 100644 index 00000000..4f2459fc --- /dev/null +++ b/Installer/Localizations/Ukrainian.lang @@ -0,0 +1,22 @@ +{ + username = "Ім'я користувача", + password = "Пароль", + submitPassword = "Надіслати пароль", + passwordsArentEqual = "Паролі не рівні", + withoutPassword = "Без пароля:", + wallpapers = "Шпалери:", + screensavers = "Заставка:", + applications = "Сторонні додатки:", + languages = "Пакети підтримки мов:", + accept = "Я приймаю це лайно:", + select = "Виберіть файлову систему для установки", + erase = "Стирати", + used = "використовуваний", + setup = "Налаштування профілю", + customize = "Налаштування завантажень", + flashing = "Миготливий EEPROM...", + creating = "Створення профілю користувача...", + installing = "Установка", + installed = "Встановлені міни. Насолоджуйтесь!", + reboot = "Перезавантажити", +} diff --git a/Installer/Main.lua b/Installer/Main.lua new file mode 100644 index 00000000..a5652161 --- /dev/null +++ b/Installer/Main.lua @@ -0,0 +1,609 @@ + +-- Backport for OpenOS non-global fields +component, computer, unicode = component or require("Component"), computer or require("Computer"), unicode or require("Unicode") + +-------------------------------------------------------------------------------- + +local EEPROMProxy, internetProxy, GPUProxy = component.proxy(component.list("eeprom")()), component.proxy(component.list("internet")()), component.proxy(component.list("gpu")()) + +local repositoryURL = "https://raw.githubusercontent.com/IgorTimofeev/MineOSStandalone/master/" +local installerURL = "Installer/" +local EFIURL = "EFI/Minified.lua" + +local installerPath = "/MineOS installer/" +local installerPicturesPath = installerPath .. "Installer/Pictures/" +local OSPath = "/" + +local screenWidth, screenHeight = GPUProxy.getResolution() + +local temporaryFilesystemProxy, currentFilesystemProxy + +-------------------------------------------------------------------------------- + +local function centrize(width) + return math.floor(screenWidth / 2 - width / 2) +end + +local function centrizedText(y, color, text) + GPUProxy.fill(1, y, screenWidth, 1, " ") + GPUProxy.setForeground(color) + GPUProxy.set(centrize(#text), y, text) +end + +local function title() + local y = math.floor(screenHeight / 2 - 1) + centrizedText(y, 0x2D2D2D, "MineOS") + return y + 2 +end + +local function status(text, needWait) + centrizedText(title(), 0x878787, text) + + if needWait then + repeat + needWait = computer.pullSignal() + until needWait == "key_down" or needWait == "touch" + end +end + +local function progress(value) + local width = 26 + local x, y, part = centrize(width), title(), math.ceil(width * value) + + GPUProxy.setForeground(0x878787) + GPUProxy.set(x, y, string.rep("─", part)) + GPUProxy.setForeground(0xC3C3C3) + GPUProxy.set(x + part, y, string.rep("─", width - part)) +end + +local function filesystemPath(path) + return path:match("^(.+%/).") or "" +end + +local function filesystemName(path) + return path:match("%/?([^%/]+)%/?$") +end + +local function filesystemHideExtension(path) + return path:match("(.+)%..+") or path +end + +local function rawRequest(url, chunkHandler) + local internetHandle, reason = internetProxy.request(repositoryURL .. url:gsub("([^%w%-%_%.%~])", function(char) + return string.format("%%%02X", string.byte(char)) + end)) + + if internetHandle then + local chunk, reason + while true do + chunk, reason = internetHandle.read(math.huge) + + if chunk then + chunkHandler(chunk) + else + if reason then + error("Internet request failed: " .. tostring(reason)) + end + + break + end + end + + internetHandle.close() + else + error("Connection failed: " .. url) + end +end + +local function request(url) + local data = "" + + rawRequest(url, function(chunk) + data = data .. chunk + end) + + return data +end + +local function download(url, path) + currentFilesystemProxy.makeDirectory(filesystemPath(path)) + + local fileHandle, reason = currentFilesystemProxy.open(path, "wb") + if fileHandle then + rawRequest(url, function(chunk) + currentFilesystemProxy.write(fileHandle, chunk) + end) + + currentFilesystemProxy.close(fileHandle) + else + error("File opening failed: " .. tostring(reason)) + end +end + +local function dofile(path, ...) + local handle, reason = currentFilesystemProxy.open(path, "rb") + if handle then + local data, chunk = "" + while true do + chunk = currentFilesystemProxy.read(handle, math.huge) + + if chunk then + data = data .. chunk + else + break + end + end + + currentFilesystemProxy.close(handle) + + return load(data, "=" .. path)(...) + end + + error("File opening failed: " .. tostring(reason)) +end + +local function deserialize(text) + local result, reason = load("return " .. text, "=string") + if result then + return result() + else + error(reason) + end +end + +-------------------------------------------------------------------------------- + +-- Clearing screen +GPUProxy.setBackground(0xE1E1E1) +GPUProxy.fill(1, 1, screenWidth, screenHeight, " ") + +-- Searching for appropriate temporary filesystem for storing libraries, images, etc +for address in component.list("filesystem") do + local proxy = component.proxy(address) + if proxy.spaceTotal() >= 2 * 1024 * 1024 then + temporaryFilesystemProxy, currentFilesystemProxy = proxy, proxy + break + end +end + +-- If there's no suitable HDDs found - then meow +if not temporaryFilesystemProxy then + status("No appropriate filesystem found", true) + return +end + +-- First, we need a big ass file list with localizations, applications, wallpapers +progress(0) +local files = deserialize(request(installerURL .. "Files.cfg")) + +-- After that we could download required libraries for installer from it +for i = 1, #files.installerFiles do + progress(i / #files.installerFiles) + download(files.installerFiles[i], installerPath .. files.installerFiles[i]) +end + +-- Initializing simple package system for loading OS libraries +package = {loading = {},loaded = {}} + +function require(module) + if package.loaded[module] then + return package.loaded[module] + elseif package.loading[module] then + error("already loading " .. module) + else + package.loading[module] = true + package.loaded[module] = dofile(installerPath .. "Libraries/" .. module .. ".lua") + package.loading[module] = nil + + return package.loaded[module] + end +end + +-- Initializing downloaded libraries +local filesystem = require("Filesystem") +filesystem.setProxy(temporaryFilesystemProxy) + +bit32 = bit32 or require("Bit32") +local image = require("Image") +local text = require("Text") +local number = require("Number") +local screen = require("Screen") +local GUI = require("GUI") +local system = require("System") + +-------------------------------------------------------------------------------- + +-- Creating out cool UI +local workspace = GUI.workspace() +workspace:addChild(GUI.panel(1, 1, workspace.width, workspace.height, 0x1E1E1E)) + +-- Main installer window +local window = workspace:addChild(GUI.window(1, 1, 80, 24)) +window.localX, window.localY = math.ceil(workspace.width / 2 - window.width / 2), math.ceil(workspace.height / 2 - window.height / 2) +window:addChild(GUI.panel(1, 1, window.width, window.height, 0xE1E1E1)) + +-- Top menu +local menu = workspace:addChild(GUI.menu(1, 1, workspace.width, 0xF0F0F0, 0x787878, 0x3366CC, 0xE1E1E1)) +local installerMenu = menu:addContextMenu("MineOS", 0x2D2D2D) +installerMenu:addItem("Shutdown").onTouch = function() + computer.shutdown() +end +installerMenu:addItem("Reboot").onTouch = function() + computer.shutdown(true) +end +installerMenu:addSeparator() +installerMenu:addItem("Exit").onTouch = function() + workspace:stop() +end + +-- Main vertical layout +local layout = window:addChild(GUI.layout(1, 1, window.width, window.height - 2, 1, 1)) + +local stageButtonsLayout = window:addChild(GUI.layout(1, window.height - 1, window.width, 1, 1, 1)) +stageButtonsLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL) +stageButtonsLayout:setSpacing(1, 1, 3) + +local function loadImage(name) + return image.load(installerPicturesPath .. name .. ".pic") +end + +local function newInput(...) + return GUI.input(1, 1, 26, 1, 0xF0F0F0, 0x787878, 0xC3C3C3, 0xF0F0F0, 0x878787, "", ...) +end + +local function newSwitchAndLabel(width, color, text, state) + return GUI.switchAndLabel(1, 1, width, 6, color, 0xD2D2D2, 0xF0F0F0, 0xA5A5A5, text .. ":", state) +end + +local function addTitle(color, text) + return layout:addChild(GUI.text(1, 1, color, text)) +end + +local function addImage(before, after, name) + if before > 0 then + layout:addChild(GUI.object(1, 1, 1, before)) + end + + local picture = layout:addChild(GUI.image(1, 1, loadImage(name))) + picture.height = picture.height + after + + return picture +end + +local function addStageButton(text) + local button = stageButtonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 2, 0, 0xC3C3C3, 0x878787, 0xA5A5A5, 0x696969, text)) + button.colors.disabled.background = 0xD2D2D2 + button.colors.disabled.text = 0xB4B4B4 + + return button +end + +local prevButton = addStageButton("<") +local nextButton = addStageButton(">") + +local localization +local stage = 1 +local stages = {} + +local usernameInput = newInput("") +local passwordInput = newInput("", false, "•") +local passwordSubmitInput = newInput("", false, "•") +local passwordMatchText = GUI.text(1, 1, 0xCC0040, "") +local passwordSwitchAndLabel = newSwitchAndLabel(26, 0x66DB80, "", false) + +local wallpapersSwitchAndLabel = newSwitchAndLabel(30, 0xFF4980, "", true) +local screensaversSwitchAndLabel = newSwitchAndLabel(30, 0xFFB600, "", true) +local applicationsSwitchAndLabel = newSwitchAndLabel(30, 0x33DB80, "", true) +local localizationsSwitchAndLabel = newSwitchAndLabel(30, 0x33B6FF, "", true) + +local acceptSwitchAndLabel = newSwitchAndLabel(30, 0x9949FF, "", false) + +local localizationComboBox = GUI.comboBox(1, 1, 22, 1, 0xF0F0F0, 0x969696, 0xD2D2D2, 0xB4B4B4) +for i = 1, #files.localizations do + localizationComboBox:addItem(filesystemHideExtension(filesystemName(files.localizations[i]))).onTouch = function() + localization = deserialize(request(installerURL .. files.localizations[i])) + + usernameInput.placeholderText = localization.username + passwordInput.placeholderText = localization.password + passwordSubmitInput.placeholderText = localization.submitPassword + passwordMatchText.text = localization.passwordsArentEqual + passwordSwitchAndLabel.label.text = localization.withoutPassword + wallpapersSwitchAndLabel.label.text = localization.wallpapers + screensaversSwitchAndLabel.label.text = localization.screensavers + applicationsSwitchAndLabel.label.text = localization.applications + localizationsSwitchAndLabel.label.text = localization.languages + acceptSwitchAndLabel.label.text = localization.accept + end +end + +local function addStage(onTouch) + table.insert(stages, function() + layout:removeChildren() + onTouch() + workspace:draw() + end) +end + +local function loadStage() + if stage < 1 then + stage = 1 + elseif stage > #stages then + stage = #stages + end + + stages[stage]() +end + +local function checkUserInputs() + nextButton.disabled = #usernameInput.text == 0 or (not passwordSwitchAndLabel.switch.state and (#passwordInput.text == 0 or #passwordSubmitInput.text == 0 or passwordInput.text ~= passwordSubmitInput.text)) + passwordMatchText.hidden = passwordSwitchAndLabel.switch.state or #passwordInput.text == 0 or #passwordSubmitInput.text == 0 or passwordInput.text == passwordSubmitInput.text +end + +local function checkLicense() + nextButton.disabled = not acceptSwitchAndLabel.switch.state +end + +prevButton.onTouch = function() + stage = stage - 1 + loadStage() +end + +nextButton.onTouch = function() + stage = stage + 1 + loadStage() +end + +acceptSwitchAndLabel.switch.onStateChanged = function() + checkLicense() + workspace:draw() +end + +passwordSwitchAndLabel.switch.onStateChanged = function() + passwordInput.hidden = passwordSwitchAndLabel.switch.state + passwordSubmitInput.hidden = passwordSwitchAndLabel.switch.state + checkUserInputs() + + workspace:draw() +end + +usernameInput.onInputFinished = function() + checkUserInputs() + workspace:draw() +end + +passwordInput.onInputFinished = usernameInput.onInputFinished +passwordSubmitInput.onInputFinished = usernameInput.onInputFinished + +-- Localization selection stage +addStage(function() + prevButton.disabled = true + + addImage(0, 1, "Languages") + layout:addChild(localizationComboBox) + + workspace:draw() + localizationComboBox:getItem(1).onTouch() +end) + +-- Filesystem selection stage +addStage(function() + prevButton.disabled = false + nextButton.disabled = false + + layout:addChild(GUI.object(1, 1, 1, 1)) + addTitle(0x696969, localization.select) + + local diskLayout = layout:addChild(GUI.layout(1, 1, layout.width, 11, 1, 1)) + diskLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL) + diskLayout:setSpacing(1, 1, 0) + + local HDDImage = loadImage("HDD") + + local function select(proxy) + currentFilesystemProxy = proxy + + for i = 1, #diskLayout.children do + diskLayout.children[i].children[1].hidden = diskLayout.children[i].proxy ~= currentFilesystemProxy + end + end + + local function updateDisks() + local function diskEventHandler(workspace, disk, e1) + if e1 == "touch" then + select(disk.proxy) + workspace:draw() + end + end + + local function addDisk(proxy, picture, disabled) + local disk = diskLayout:addChild(GUI.container(1, 1, 14, diskLayout.height)) + + local formatContainer = disk:addChild(GUI.container(1, 1, disk.width, disk.height)) + formatContainer:addChild(GUI.panel(1, 1, formatContainer.width, formatContainer.height, 0xD2D2D2)) + formatContainer:addChild(GUI.button(1, formatContainer.height, formatContainer.width, 1, 0xCC4940, 0xE1E1E1, 0x990000, 0xE1E1E1, localization.erase)).onTouch = function() + local list, path = proxy.list("/") + for i = 1, #list do + path = "/" .. list[i] + if path ~= installerPath then + proxy.remove(path) + end + end + + updateDisks() + end + + if disabled then + picture = image.blend(picture, 0xFFFFFF, 0.4) + disk.disabled = true + end + + disk:addChild(GUI.image(4, 2, picture)) + disk:addChild(GUI.label(2, 7, disk.width - 2, 1, disabled and 0x969696 or 0x696969, text.limit(proxy.getLabel() or proxy.address, disk.width - 2))):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + disk:addChild(GUI.progressBar(2, 8, disk.width - 2, disabled and 0xCCDBFF or 0x66B6FF, disabled and 0xD2D2D2 or 0xC3C3C3, disabled and 0xC3C3C3 or 0xA5A5A5, math.floor(proxy.spaceUsed() / proxy.spaceTotal() * 100), true, true, "", "% " .. localization.used)) + + disk.eventHandler = diskEventHandler + disk.proxy = proxy + end + + diskLayout:removeChildren() + + for address in component.list("filesystem") do + local proxy = component.proxy(address) + if proxy.spaceTotal() >= 1 * 1024 * 1024 then + addDisk( + proxy, + proxy.spaceTotal() < 1 * 1024 * 1024 and floppyImage or HDDImage, + proxy.isReadOnly() or proxy.spaceTotal() < 2 * 1024 * 1024 + ) + end + end + + select(currentFilesystemProxy) + end + + updateDisks() +end) + +-- User profile setup stage +addStage(function() + checkUserInputs() + + addImage(0, 0, "User") + addTitle(0x696969, localization.setup) + + layout:addChild(usernameInput) + layout:addChild(passwordInput) + layout:addChild(passwordSubmitInput) + layout:addChild(passwordMatchText) + layout:addChild(passwordSwitchAndLabel) +end) + +-- Downloads customization stage +addStage(function() + nextButton.disabled = false + + addImage(0, 0, "Settings") + addTitle(0x696969, localization.customize) + + layout:addChild(wallpapersSwitchAndLabel) + layout:addChild(screensaversSwitchAndLabel) + layout:addChild(applicationsSwitchAndLabel) + layout:addChild(localizationsSwitchAndLabel) +end) + +-- License acception stage +addStage(function() + checkLicense() + + local lines = text.wrap({request(repositoryURL .. "LICENSE")}, layout.width - 2) + local textBox = layout:addChild(GUI.textBox(1, 1, layout.width, layout.height - 3, 0xF0F0F0, 0x696969, lines, 1, 1, 1)) + + layout:addChild(acceptSwitchAndLabel) +end) + +-- Downloading stage +addStage(function() + stageButtonsLayout:removeChildren() + + -- Creating user profile + layout:removeChildren() + addImage(1, 1, "User") + addTitle(0x969696, localization.creating) + workspace:draw() + + -- Creating system paths + paths.create(paths.system) + + -- Creating user paths + local properties, paths = system.createUser( + usernameInput.text, + filesystemHideExtension(filesystemName(files.localizations[localizationComboBox.selectedItem])), + not passwordSwitchAndLabel.switch.state and passwordInput.text, + wallpapersSwitchAndLabel.switch.state, + screensaversSwitchAndLabel.switch.state + ) + + -- Flashing EEPROM + layout:removeChildren() + addImage(1, 1, "EEPROM") + addTitle(0x969696, localization.flashing) + workspace:draw() + + EEPROMProxy.set(request(EFIURL)) + EEPROMProxy.setLabel("MineOS EFI") + EEPROMProxy.setData(currentFilesystemProxy.address) + + -- Creating list of files to download + layout:removeChildren() + addImage(3, 2, "Downloading") + + local container = layout:addChild(GUI.container(1, 1, layout.width - 20, 2)) + local progressBar = container:addChild(GUI.progressBar(1, 1, container.width, 0x66B6FF, 0xD2D2D2, 0xA5A5A5, 0, true, false)) + local cyka = container:addChild(GUI.label(1, 2, container.width, 1, 0x969696, "")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + + local downloadList = files.required + + local function addToList(switchAndLabel, key) + if switchAndLabel.switch.state then + for i = 1, #files[key] do + table.insert(downloadList, files[key][i]) + end + end + end + + addToList(wallpapersSwitchAndLabel, "optional") + addToList(wallpapersSwitchAndLabel, "wallpapers") + addToList(screensaversSwitchAndLabel, "screensavers") + + if localizationsSwitchAndLabel.switch.state then + for i = 1, #files.localizations do + table.insert(downloadList, files.localizations[i]) + end + else + table.insert(downloadList, files.localizations[localizationComboBox.selectedItem]) + end + + -- Downloading files from created list + local path, id, shortcut + for i = 1, #downloadList do + if type(downloadList[i]) == "table" then + path, id, shortcut = downloadList[i].path, downloadList[i].id, downloadList[i].shortcut + else + path, id, shortcut = downloadList[i] + end + + cyka.text = text.limit(localization.installing .. " \"" .. path .. "\"", container.width, "center") + workspace:draw() + + -- Download file + download(path, OSPath .. path) + + -- Create shortcut if possible + if shortcut then + system.createShortcut( + paths.desktop .. filesystemHideExtension(filesystemName(filesystemPath(path)) .. "/") .. ".lnk", + OSPath .. filesystemPath(path) + ) + end + + progressBar.value = math.floor(i / #downloadList * 100) + workspace:draw() + end + + -- Done info + layout:removeChildren() + addImage(1, 1, "Done") + addTitle(0x969696, localization.installed) + addStageButton(localization.reboot).onTouch = function() + computer.shutdown(true) + end + workspace:draw() + + -- Removing temporary installer directory + temporaryFilesystemProxy.remove(installerPath) +end) + +-------------------------------------------------------------------------------- + +loadStage() +workspace:start() diff --git a/Installer/Pictures/Done.pic b/Installer/Pictures/Done.pic new file mode 100644 index 0000000000000000000000000000000000000000..0571c2952fc7c2337c5e8175f1f59e7abbbc70dd GIT binary patch literal 1064 zcmZWoJ8KkC7~OO4ow@Vco!yz;?Ce7jH;JWL#Oq3PS#^9w`^67sLypvpN=8y#CGS*;e}!P z$M8dvm_-6Rqog(SglRS7A7_-!2G3qGOP}}xi9|~wc0|453MbO=JOn;kA>vwXVDcx< zJ|+Ul1o{h@z=Jo!0(`g^Xlb|#t8hfSWAZnc(j_o38_UiJdrXQmY%?i2!Z9L*j~Q3U z_L(Ro-)D+trRa0f9ua)VGKAikRsh$m@9J;-6iB}l6q;8F4&qfD(n_Aw^1$8`0G&$& z4QYy;)5q{7*ESW0kZ4c%GgvIMTUQ7U<~H%3BN}F7S00u1y{cX>m1({yo{?J$S90-X z8_W138}&DF!cs58WoTtD!{kq0%0v=#3v_zIaTmpHQj;{GJ~Ka*eh^h!wbD{SUYBQz zdupDAK7=I^A^1TE;gyroH0x{PfF6mYL2${pc&T>ysnDBJ1c_eBM)G7bx;0W$ypkvU z457CO0n`^l1mENjDa<^F>5?UMrHEK%T-h_=CXtxp1u7PGlfOo_n7|ZU!tUP(#=>@W ztOnZTk7Sh*R@V_(9R%7Yf50=b&##+eh3)T>eQfeIQOR7Nw?RquOnzTnW03~gEpY&e zu2A|@l4(xylW$D-ITGj|vva4)Ri=1*b~~L9tdfL-c83Um1E3MaOaK4? literal 0 HcmV?d00001 diff --git a/Installer/Pictures/Downloading.pic b/Installer/Pictures/Downloading.pic new file mode 100644 index 0000000000000000000000000000000000000000..51055320dea8d92472e64b37563e2d189b3d9b8f GIT binary patch literal 536 zcmZWlNlpVX5OlTISppf)l4P>^ns*=}apFypxbOxJL<%R!og;slj|}aZi4e-6RpoMZ zb=%JmPmd-z>V@l0NA&awjr<6Mv^)P$k1r@nQUTT(yVLvR$)YFe&L5-4f~3;Mir4_o zdS#t^{`45^y7LS7Um%f2UmmDee4%D{_w$Ryd`fXin&nZW8%540&uHM3q4_B7jn(|O zx*LOlAPHyUUfI)Y zM{s#jR&}#lZ??OAh-p9_sG-ad`AWhoc+G=CC79ddk~b@xM9Z0ee42AzvM%Zg#InB=rsxL@6eA1X=oquYzauc2+bul zQ2`ga6SE|)6f@#N2}_iOKQ+ihcb2Sij%=tPs1$mx(5of0de&;c+hm%A_7&4>E%vpR zCH5sjmBML|)u2h}PR)H5vqP~QbG>Czd*bV!F?^rlN$7rp2Bnx6ScLX#O~#-kVwQ0o zx+5%QMbPO!IWnO;PVb?xmXv$I$b6BrQ7rz#-$Yqm;62&!Y(D{J4(y0}kh_0auGqx` z-Yr7+JmR;C{}F3Z4$N=xFCW>U%XlfulwA>8;gq~LW+13mmOY}H4s~cd=2Ep?=&zy# z32f*uMekYI4%v;(0Y^OB}$PDEQz;M5Qvgz=gV+h)mGXn&QZ zs0@_?Ei^-^H;$qRf^>-C#kV-cHM7*AIW(W+BUY-ErwslMSt_YZd%V%ftAVS~zG1`- z5-G~i{kCxqQ$3GJOSRk;i>r<6!2z_*B6OFL=3FMVC7uj((kKQtp>NukQ8vR-X}@Cg zIzUzEhUQB)-~SLVIBjli&m!gtm1F{XVY)9FQXhTK%(FNnaY*^b((lYLax)#N0{%}G zfwHy-%(@bw%ykSN^G#VBhwjy;8J@l)7Dh?I*1z8zBQy=^QV)$C#)An<+L?~zL2h9T zD28L&{>i|RWaR6m8OFUlL$%5^9%Wb8 zxlVK_qjL((?~(wa3e7jkHBD!cMcO3GQ{*~i9x|{|=ZtUupS%T;vbHa(l-=h@bxOvA zyxe=5iDQt8U=Os@YBkswCy{lTCImw?8=fnoP4Z fbs73Iy(k5wNEx2u@Bh46 zu2((^eH=7(aNH6B47?$L2nf+0BMPE(GfijA>yR`*DuSfZ>$5U}G+nT;1jB3pN7j78 zV7~;>w6$z&8sZY)Tf8-!*L0t-(f%;?1 literal 0 HcmV?d00001 diff --git a/Installer/Pictures/Languages.pic b/Installer/Pictures/Languages.pic new file mode 100644 index 0000000000000000000000000000000000000000..562787b44de09af434046b77041fb163b3ea983a GIT binary patch literal 5325 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 T(Q(3PLiFof!{B?U_oVyZXpk-a literal 0 HcmV?d00001 diff --git a/Installer/Pictures/Settings.pic b/Installer/Pictures/Settings.pic new file mode 100644 index 0000000000000000000000000000000000000000..6add2e9dafbde5e0ec6cdd9b6f4eeb0ffae40adb GIT binary patch literal 293 zcmXw!F$%&!5JfYSO|nO@wDlZ@iju2=p-6|U`^#uZA=`jn$udF{8>_Nr7`qcVYWtn!ZY_K#HsDjvv zV;pC0iq5TVII^{aXYaT`v0*B#g8s`a5DhjiwB}g)RJe}v`Mg$o7O5b#83?DLf){=2 GhyDQ)i9S>S literal 0 HcmV?d00001 diff --git a/LICENSE b/LICENSE index 3174f454..ab602974 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 Igor Timofeev +Copyright (c) 2018 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Libraries/.palette.cfg b/Libraries/.palette.cfg new file mode 100644 index 00000000..faf937e9 --- /dev/null +++ b/Libraries/.palette.cfg @@ -0,0 +1 @@ +{[1]=16717056,[2]=16122111,[3]=65377,[4]=65280,[5]=7536384,[6]=16719104} \ No newline at end of file diff --git a/lib/archive.lua b/Libraries/Archive.lua similarity index 95% rename from lib/archive.lua rename to Libraries/Archive.lua index 3ac0a294..af6a5c9e 100755 --- a/lib/archive.lua +++ b/Libraries/Archive.lua @@ -1,7 +1,7 @@ -local fs = require("filesystem") -local computer = require("computer") -local component = require("component") +local fs = require("Filesystem") +local computer = require("Computer") +local component = require("Component") ----------------------------------------------------------------------------------------------- diff --git a/lib/bigLetters.lua b/Libraries/BigLetters.lua similarity index 98% rename from lib/bigLetters.lua rename to Libraries/BigLetters.lua index 6647e225..acc59e1a 100755 --- a/lib/bigLetters.lua +++ b/Libraries/BigLetters.lua @@ -1,6 +1,5 @@ -local unicode = require("unicode") -local buffer = require("doubleBuffering") +local screen = require("Screen") local bigLetters = {} local pixelHeight = 5 @@ -558,9 +557,9 @@ function bigLetters.draw(x, y, color, symbol, drawWithSymbol) for i = 1, #letters[symbol][j] do if letters[symbol][j][i] == 1 then if not drawWithSymbol then - buffer.drawRectangle(x + i * 2 - 2, y + (pixelHeight - #letters[symbol]) + j - 1, 2, 1, color, 0xFFFFFF, " ") + screen.drawRectangle(x + i * 2 - 2, y + (pixelHeight - #letters[symbol]) + j - 1, 2, 1, color, 0xFFFFFF, " ") else - buffer.drawText(x + i * 2 - 2, y + (pixelHeight - #letters[symbol]) + j - 1, color, "*") + screen.drawText(x + i * 2 - 2, y + (pixelHeight - #letters[symbol]) + j - 1, color, "*") end end end diff --git a/Libraries/Bit32.lua b/Libraries/Bit32.lua new file mode 100755 index 00000000..67bbcffe --- /dev/null +++ b/Libraries/Bit32.lua @@ -0,0 +1,102 @@ +--[[ Backwards compat for Lua 5.3; only loaded in 5.3 because package.loaded is + prepopulated with the existing global bit32 in 5.2. ]] + +local bit32 = {} + +------------------------------------------------------------------------------- + +local function fold(init, op, ...) + local result = init + local args = table.pack(...) + for i = 1, args.n do + result = op(result, args[i]) + end + return result +end + +local function trim(n) + return n & 0xFFFFFFFF +end + +local function mask(w) + return ~(0xFFFFFFFF << w) +end + +function bit32.arshift(x, disp) + return x // (2 ^ disp) +end + +function bit32.band(...) + return fold(0xFFFFFFFF, function(a, b) return a & b end, ...) +end + +function bit32.bnot(x) + return ~x +end + +function bit32.bor(...) + return fold(0, function(a, b) return a | b end, ...) +end + +function bit32.btest(...) + return bit32.band(...) ~= 0 +end + +function bit32.bxor(...) + return fold(0, function(a, b) return a ~ b end, ...) +end + +local function fieldargs(f, w) + w = w or 1 + assert(f >= 0, "field cannot be negative") + assert(w > 0, "width must be positive") + assert(f + w <= 32, "trying to access non-existent bits") + return f, w +end + +function bit32.extract(n, field, width) + local f, w = fieldargs(field, width) + return (n >> f) & mask(w) +end + +function bit32.replace(n, v, field, width) + local f, w = fieldargs(field, width) + local m = mask(w) + return (n & ~(m << f)) | ((v & m) << f) +end + +function bit32.lrotate(x, disp) + if disp == 0 then + return x + elseif disp < 0 then + return bit32.rrotate(x, -disp) + else + disp = disp & 31 + x = trim(x) + return trim((x << disp) | (x >> (32 - disp))) + end +end + +function bit32.lshift(x, disp) + return trim(x << disp) +end + +function bit32.rrotate(x, disp) + if disp == 0 then + return x + elseif disp < 0 then + return bit32.lrotate(x, -disp) + else + disp = disp & 31 + x = trim(x) + return trim((x >> disp) | (x << (32 - disp))) + end +end + +function bit32.rshift(x, disp) + return trim(x >> disp) +end + +------------------------------------------------------------------------------- + +return bit32 diff --git a/Libraries/Color.lua b/Libraries/Color.lua new file mode 100755 index 00000000..31c02008 --- /dev/null +++ b/Libraries/Color.lua @@ -0,0 +1,229 @@ + +local palette = {0x000000, 0x000040, 0x000080, 0x0000BF, 0x0000FF, 0x002400, 0x002440, 0x002480, 0x0024BF, 0x0024FF, 0x004900, 0x004940, 0x004980, 0x0049BF, 0x0049FF, 0x006D00, 0x006D40, 0x006D80, 0x006DBF, 0x006DFF, 0x009200, 0x009240, 0x009280, 0x0092BF, 0x0092FF, 0x00B600, 0x00B640, 0x00B680, 0x00B6BF, 0x00B6FF, 0x00DB00, 0x00DB40, 0x00DB80, 0x00DBBF, 0x00DBFF, 0x00FF00, 0x00FF40, 0x00FF80, 0x00FFBF, 0x00FFFF, 0x0F0F0F, 0x1E1E1E, 0x2D2D2D, 0x330000, 0x330040, 0x330080, 0x3300BF, 0x3300FF, 0x332400, 0x332440, 0x332480, 0x3324BF, 0x3324FF, 0x334900, 0x334940, 0x334980, 0x3349BF, 0x3349FF, 0x336D00, 0x336D40, 0x336D80, 0x336DBF, 0x336DFF, 0x339200, 0x339240, 0x339280, 0x3392BF, 0x3392FF, 0x33B600, 0x33B640, 0x33B680, 0x33B6BF, 0x33B6FF, 0x33DB00, 0x33DB40, 0x33DB80, 0x33DBBF, 0x33DBFF, 0x33FF00, 0x33FF40, 0x33FF80, 0x33FFBF, 0x33FFFF, 0x3C3C3C, 0x4B4B4B, 0x5A5A5A, 0x660000, 0x660040, 0x660080, 0x6600BF, 0x6600FF, 0x662400, 0x662440, 0x662480, 0x6624BF, 0x6624FF, 0x664900, 0x664940, 0x664980, 0x6649BF, 0x6649FF, 0x666D00, 0x666D40, 0x666D80, 0x666DBF, 0x666DFF, 0x669200, 0x669240, 0x669280, 0x6692BF, 0x6692FF, 0x66B600, 0x66B640, 0x66B680, 0x66B6BF, 0x66B6FF, 0x66DB00, 0x66DB40, 0x66DB80, 0x66DBBF, 0x66DBFF, 0x66FF00, 0x66FF40, 0x66FF80, 0x66FFBF, 0x66FFFF, 0x696969, 0x787878, 0x878787, 0x969696, 0x990000, 0x990040, 0x990080, 0x9900BF, 0x9900FF, 0x992400, 0x992440, 0x992480, 0x9924BF, 0x9924FF, 0x994900, 0x994940, 0x994980, 0x9949BF, 0x9949FF, 0x996D00, 0x996D40, 0x996D80, 0x996DBF, 0x996DFF, 0x999200, 0x999240, 0x999280, 0x9992BF, 0x9992FF, 0x99B600, 0x99B640, 0x99B680, 0x99B6BF, 0x99B6FF, 0x99DB00, 0x99DB40, 0x99DB80, 0x99DBBF, 0x99DBFF, 0x99FF00, 0x99FF40, 0x99FF80, 0x99FFBF, 0x99FFFF, 0xA5A5A5, 0xB4B4B4, 0xC3C3C3, 0xCC0000, 0xCC0040, 0xCC0080, 0xCC00BF, 0xCC00FF, 0xCC2400, 0xCC2440, 0xCC2480, 0xCC24BF, 0xCC24FF, 0xCC4900, 0xCC4940, 0xCC4980, 0xCC49BF, 0xCC49FF, 0xCC6D00, 0xCC6D40, 0xCC6D80, 0xCC6DBF, 0xCC6DFF, 0xCC9200, 0xCC9240, 0xCC9280, 0xCC92BF, 0xCC92FF, 0xCCB600, 0xCCB640, 0xCCB680, 0xCCB6BF, 0xCCB6FF, 0xCCDB00, 0xCCDB40, 0xCCDB80, 0xCCDBBF, 0xCCDBFF, 0xCCFF00, 0xCCFF40, 0xCCFF80, 0xCCFFBF, 0xCCFFFF, 0xD2D2D2, 0xE1E1E1, 0xF0F0F0, 0xFF0000, 0xFF0040, 0xFF0080, 0xFF00BF, 0xFF00FF, 0xFF2400, 0xFF2440, 0xFF2480, 0xFF24BF, 0xFF24FF, 0xFF4900, 0xFF4940, 0xFF4980, 0xFF49BF, 0xFF49FF, 0xFF6D00, 0xFF6D40, 0xFF6D80, 0xFF6DBF, 0xFF6DFF, 0xFF9200, 0xFF9240, 0xFF9280, 0xFF92BF, 0xFF92FF, 0xFFB600, 0xFFB640, 0xFFB680, 0xFFB6BF, 0xFFB6FF, 0xFFDB00, 0xFFDB40, 0xFFDB80, 0xFFDBBF, 0xFFDBFF, 0xFFFF00, 0xFFFF40, 0xFFFF80, 0xFFFFBF, 0xFFFFFF} +local mathFloor, mathMax, mathMin, mathModf = math.floor, math.max, math.min, math.modf +local integerToRGB, RGBToInteger, blend, transition, to8Bit + +local color = {} + +-------------------------------------------------------------------------------- + +-- Optimized Lua 5.3 bitwise support +if computer.getArchitecture and computer.getArchitecture() == "Lua 5.3" then + integerToRGB, RGBToInteger, blend, transition, to8Bit = load([[ + local mathHuge, palette = math.huge, select(1, ...) + + return + function(integerColor) + return integerColor >> 16, integerColor >> 8 & 0xFF, integerColor & 0xFF + end, + + function(r, g, b) + return r << 16 | g << 8 | b + end, + + function(color1, color2, transparency) + local invertedTransparency = 1 - transparency + return + ((color2 >> 16) * invertedTransparency + (color1 >> 16) * transparency) // 1 << 16 | + ((color2 >> 8 & 0xFF) * invertedTransparency + (color1 >> 8 & 0xFF) * transparency) // 1 << 8 | + ((color2 & 0xFF) * invertedTransparency + (color1 & 0xFF) * transparency) // 1 + end, + + function(color1, color2, position) + local r1, g1, b1 = color1 >> 16, color1 >> 8 & 0xFF, color1 & 0xFF + return + (r1 + ((color2 >> 16) - r1) * position) // 1 << 16 | + (g1 + ((color2 >> 8 & 0xFF) - g1) * position) // 1 << 8 | + (b1 + ((color2 & 0xFF) - b1) * position) // 1 + end, + + function(color24Bit) + local r, g, b, closestDelta, closestIndex, delta, paletteColor, paletteR, paletteG, paletteB = color24Bit >> 16, color24Bit >> 8 & 0xFF, color24Bit & 0xFF, mathHuge, 1 + + for i = 1, #palette do + paletteColor = palette[i] + + if color24Bit == paletteColor then + return i - 1 + else + paletteR, paletteG, paletteB = paletteColor >> 16, paletteColor >> 8 & 0xFF, paletteColor & 0xFF + + delta = (paletteR - r) ^ 2 + (paletteG - g) ^ 2 + (paletteB - b) ^ 2 + if delta < closestDelta then + closestDelta, closestIndex = delta, i + end + end + end + + return closestIndex - 1 + end + ]])(palette) +else + integerToRGB, RGBToInteger, blend, transition, to8Bit = load([[ + local mathHuge, palette = math.huge, select(1, ...) + + return + function(integerColor) + local r = integerColor / 65536 + r = r - r % 1 + local g = (integerColor - r * 65536) / 256 + g = g - g % 1 + + return r, g, integerColor - r * 65536 - g * 256 + end, + + function(r, g, b) + return r * 65536 + g * 256 + b + end, + + function(color1, color2, transparency) + local invertedTransparency = 1 - transparency + + local r1 = color1 / 65536 + r1 = r1 - r1 % 1 + local g1 = (color1 - r1 * 65536) / 256 + g1 = g1 - g1 % 1 + + local r2 = color2 / 65536 + r2 = r2 - r2 % 1 + local g2 = (color2 - r2 * 65536) / 256 + g2 = g2 - g2 % 1 + + local r, g, b = + r2 * invertedTransparency + r1 * transparency, + g2 * invertedTransparency + g1 * transparency, + (color2 - r2 * 65536 - g2 * 256) * invertedTransparency + (color1 - r1 * 65536 - g1 * 256) * transparency + + return + (r - r % 1) * 65536 + + (g - g % 1) * 256 + + (b - b % 1) + end, + + function(color1, color2, position) + local r1 = color1 / 65536 + r1 = r1 - r1 % 1 + local g1 = (color1 - r1 * 65536) / 256 + g1 = g1 - g1 % 1 + local b1 = color1 - r1 * 65536 - g1 * 256 + + local r2 = color2 / 65536 + r2 = r2 - r2 % 1 + local g2 = (color2 - r2 * 65536) / 256 + g2 = g2 - g2 % 1 + + local r, g, b = + r1 + (r2 - r1) * position, + g1 + (g2 - g1) * position, + b1 + (color2 - r2 * 65536 - g2 * 256 - b1) * position + + return + (r - r % 1) * 65536 + + (g - g % 1) * 256 + + (b - b % 1) + end, + + function(color24Bit) + local closestDelta, closestIndex, delta, paletteColor, paletteR, paletteG, paletteB = mathHuge, 1 + + local r = color24Bit / 65536 + r = r - r % 1 + local g = (color24Bit - r * 65536) / 256 + g = g - g % 1 + local b = color24Bit - r * 65536 - g * 256 + + for index = 1, #palette do + paletteColor = palette[index] + if color24Bit == paletteColor then + return index - 1 + else + paletteR = paletteColor / 65536 + paletteR = paletteR - paletteR % 1 + paletteG = (paletteColor - paletteR * 65536) / 256 + paletteG = paletteG - paletteG % 1 + paletteB = paletteColor - paletteR * 65536 - paletteG * 256 + + delta = (paletteR - r) ^ 2 + (paletteG - g) ^ 2 + (paletteB - b) ^ 2 + if delta < closestDelta then + closestDelta, closestIndex = delta, index + end + end + end + + return closestIndex - 1 + end + ]])(palette) +end + +-------------------------------------------------------------------------------- + +local function RGBToHSB(r, g, b) + local max, min = mathMax(r, g, b), mathMin(r, g, b) + + if max == min then + return 0, max == 0 and 0 or (1 - min / max), max / 255 + elseif max == r and g >= b then + return 60 * (g - b) / (max - min), max == 0 and 0 or (1 - min / max), max / 255 + elseif max == r and g < b then + return 60 * (g - b) / (max - min) + 360, max == 0 and 0 or (1 - min / max), max / 255 + elseif max == g then + return 60 * (b - r) / (max - min) + 120, max == 0 and 0 or (1 - min / max), max / 255 + elseif max == b then + return 60 * (r - g) / (max - min) + 240, max == 0 and 0 or (1 - min / max), max / 255 + else + return 0, max == 0 and 0 or (1 - min / max), max / 255 + end +end + +local function HSBToRGB(h, s, b) + local integer, fractional = mathModf(h / 60) + local p, q, t = b * (1 - s), b * (1 - s * fractional), b * (1 - (1 - fractional) * s) + + if integer == 0 then + return mathFloor(b * 255), mathFloor(t * 255), mathFloor(p * 255) + elseif integer == 1 then + return mathFloor(q * 255), mathFloor(b * 255), mathFloor(p * 255) + elseif integer == 2 then + return mathFloor(p * 255), mathFloor(b * 255), mathFloor(t * 255) + elseif integer == 3 then + return mathFloor(p * 255), mathFloor(q * 255), mathFloor(b * 255) + elseif integer == 4 then + return mathFloor(t * 255), mathFloor(p * 255), mathFloor(b * 255) + else + return mathFloor(b * 255), mathFloor(p * 255), mathFloor(q * 255) + end +end + +local function integerToHSB(integerColor) + return RGBToHSB(integerToRGB(integerColor)) +end + +local function HSBToInteger(h, s, b) + return RGBToInteger(HSBToRGB(h, s, b)) +end + +-------------------------------------------------------------------------------- + +local function to24Bit(color8Bit) + return palette[color8Bit + 1] +end + +local function optimize(color24Bit) + return to24Bit(to8Bit(color24Bit)) +end + +-------------------------------------------------------------------------------- + +return { + RGBToInteger = RGBToInteger, + integerToRGB = integerToRGB, + RGBToHSB = RGBToHSB, + HSBToRGB = HSBToRGB, + integerToHSB = integerToHSB, + HSBToInteger = HSBToInteger, + blend = blend, + transition = transition, + to8Bit = to8Bit, + to24Bit = to24Bit, + optimize = optimize, +} \ No newline at end of file diff --git a/Libraries/Component.lua b/Libraries/Component.lua new file mode 100644 index 00000000..b86952b3 --- /dev/null +++ b/Libraries/Component.lua @@ -0,0 +1,13 @@ + +function component.get(type) + local address = component.list(type)() + if address then + return component.proxy(address) + end + + return nil, "component with type \"" .. type .. "\" doesn't exists" +end + +function component.isAvailable(type) + return component.list(type)() and true or false +end diff --git a/Libraries/Event.lua b/Libraries/Event.lua new file mode 100755 index 00000000..f8a6b27f --- /dev/null +++ b/Libraries/Event.lua @@ -0,0 +1,134 @@ + +local event, handlers, interruptingKeysDown, lastInterrupt = { + interruptingEnabled = true, + interruptingDelay = 1, + interruptingKeyCodes = { + [29] = true, + [46] = true, + [56] = true + }, + push = computer.pushSignal +}, {}, {}, 0 + +local computerPullSignal, computerUptime, mathHuge, mathMin, skipSignalType = computer.pullSignal, computer.uptime, math.huge, math.min + +-------------------------------------------------------------------------------------------------------- + +function event.addHandler(callback, interval, times) + checkArg(1, callback, "function") + checkArg(2, interval, "number", "nil") + checkArg(3, times, "number", "nil") + + local handler = { + callback = callback, + times = times or mathHuge, + interval = interval, + nextTriggerTime = interval and computerUptime() + interval or 0 + } + + handlers[handler] = true + + return handler +end + +function event.removeHandler(handler) + checkArg(1, handler, "table") + + if handlers[handler] then + handlers[handler] = nil + + return true + else + return false, "Handler with given table is not registered" + end +end + +function event.getHandlers() + return handlers +end + +function event.skip(signalType) + skipSignalType = signalType +end + +function event.pull(preferredTimeout) + local uptime = computerUptime() + local deadline = uptime + (preferredTimeout or mathHuge) + + repeat + -- Determining pullSignal timeout + timeout = deadline + for handler in pairs(handlers) do + if handler.nextTriggerTime > 0 then + timeout = mathMin(timeout, handler.nextTriggerTime) + end + end + + -- Pulling signal data + signalData = { computerPullSignal(timeout - computerUptime()) } + + -- Handlers processing + for handler in pairs(handlers) do + if handler.times > 0 then + uptime = computerUptime() + + if + handler.nextTriggerTime <= uptime + then + handler.times = handler.times - 1 + if handler.nextTriggerTime > 0 then + handler.nextTriggerTime = uptime + handler.interval + end + + -- Callback running + handler.callback(table.unpack(signalData)) + end + else + handlers[handler] = nil + end + end + + -- Program interruption support. It's faster to do it here instead of registering handlers + if signalData[1] == "key_down" or signalData[1] == "key_up" and event.interruptingEnabled then + -- Analysing for which interrupting key is pressed - we don't need keyboard API for this + if event.interruptingKeyCodes[signalData[4]] then + interruptingKeysDown[signalData[4]] = signalData[1] == "key_down" and true or nil + end + + local shouldInterrupt = true + for keyCode in pairs(event.interruptingKeyCodes) do + if not interruptingKeysDown[keyCode] then + shouldInterrupt = false + end + end + + if shouldInterrupt and uptime - lastInterrupt > event.interruptingDelay then + lastInterrupt = uptime + error("interrupted", 0) + end + end + + -- Loop-breaking condition + if signalData[1] then + if signalData[1] == skipSignalType then + skipSignalType = nil + else + return table.unpack(signalData) + end + end + until uptime >= deadline +end + +-- Sleeps "time" of seconds via "busy-wait" concept +function event.sleep(time) + checkArg(1, time, "number", "nil") + + local deadline = computerUptime() + (time or 0) + repeat + event.pull(deadline - computerUptime()) + until computerUptime() >= deadline +end + +-------------------------------------------------------------------------------------------------------- + +return event diff --git a/Libraries/Filesystem.lua b/Libraries/Filesystem.lua new file mode 100644 index 00000000..3735551d --- /dev/null +++ b/Libraries/Filesystem.lua @@ -0,0 +1,655 @@ + +local paths = require("Paths") +local event = require("Event") + +-------------------------------------------------------------------------------- + +local filesystem = { + SORTING_NAME = 1, + SORTING_TYPE = 2, + SORTING_DATE = 3, +} + +local BUFFER_SIZE = 1024 +local BOOT_PROXY + +local mountedProxies = {} + +--------------------------------------- String-related path processing ----------------------------------------- + +function filesystem.path(path) + return path:match("^(.+%/).") or "" +end + +function filesystem.name(path) + return path:match("%/?([^%/]+)%/?$") +end + +function filesystem.extension(path, lower) + return path:match("[^%/]+(%.[^%/]+)%/?$") +end + +function filesystem.hideExtension(path) + return path:match("(.+)%..+") or path +end + +function filesystem.isHidden(path) + if path:sub(1, 1) == "." then + return true + end + + return false +end + +function filesystem.removeSlashes(path) + return path:gsub("/+", "/") +end + +--------------------------------------- Mounted filesystem support ----------------------------------------- + +function filesystem.mount(cyka, path) + if type(cyka) == "table" then + for i = 1, #mountedProxies do + if mountedProxies[i].path == path then + return false, "mount path has been taken by other mounted filesystem" + elseif mountedProxies[i].proxy == cyka then + return false, "proxy is already mounted" + end + end + + table.insert(mountedProxies, { + path = path, + proxy = cyka + }) + + return true + else + error("bad argument #1 (filesystem proxy expected, got " .. tostring(cyka) .. ")") + end +end + +function filesystem.unmount(cyka) + if type(cyka) == "table" then + for i = 1, #mountedProxies do + if mountedProxies[i].proxy == cyka then + table.remove(mountedProxies, i) + return true + end + end + + return false, "specified proxy is not mounted" + elseif type(cyka) == "string" then + for i = 1, #mountedProxies do + if mountedProxies[i].proxy.address == cyka then + table.remove(mountedProxies, i) + return true + end + end + + return false, "specified proxy address is not mounted" + else + error("bad argument #1 (filesystem proxy or mounted path expected, got " .. tostring(cyka) .. ")") + end +end + +function filesystem.get(path) + checkArg(1, path, "string") + + for i = 1, #mountedProxies do + if path:sub(1, unicode.len(mountedProxies[i].path)) == mountedProxies[i].path then + return mountedProxies[i].proxy, unicode.sub(path, mountedProxies[i].path:len() + 1, -1) + end + end + + return BOOT_PROXY, path +end + +function filesystem.mounts() + local key, value + return function() + key, value = next(mountedProxies, key) + if value then + return value.proxy, value.path + end + end +end + +--------------------------------------- I/O methods ----------------------------------------- + +local function readAndReposition(self, count) + local data = self.proxy.read(self.stream, count) + if data then + self.position = self.position + #data + end + + return data +end + +local function readString(self, count) + if count > #self.buffer then + local data, chunk = self.buffer + while #data < count do + chunk = readAndReposition(self, BUFFER_SIZE) + + if chunk then + data = data .. chunk + else + self.buffer = "" + -- EOF at start + if data == "" then + return nil + -- EOF after read + else + return data + end + end + end + + self.buffer = data:sub(count + 1, -1) + + return data:sub(1, count) + else + local data = self.buffer:sub(1, count) + self.buffer = self.buffer:sub(count + 1, -1) + + return data + end +end + +local function readLine(self) + local data = "" + while true do + if #self.buffer > 0 then + local starting, ending = self.buffer:find("\n") + if starting then + data = data .. self.buffer:sub(1, starting - 1) + self.buffer = self.buffer:sub(ending + 1, -1) + + return data + else + data = data .. self.buffer + end + end + + local chunk = readAndReposition(self, BUFFER_SIZE) + if chunk then + self.buffer = chunk + -- EOF + else + local data = self.buffer + self.buffer = "" + + return #data > 0 and data or nil + end + end +end + +local function lines(self) + return function() + local line = readLine(self) + if line then + return line + else + self:close() + end + end +end + +local function readAll(self) + local data, chunk = "" + while true do + chunk = readAndReposition(self, 4096) + if chunk then + data = data .. chunk + else + return data + end + end +end + +local function readBytes(self, count, littleEndian) + if count == 1 then + local data = readString(self, 1) + if data then + return string.byte(data) + end + + return nil + else + local bytes, result = {string.byte(readString(self, count) or "\x00", 1, 8)}, 0 + + if littleEndian then + for i = #bytes, 1, -1 do + result = bit32.bor(bit32.lshift(result, 8), bytes[i]) + end + else + for i = 1, #bytes do + result = bit32.bor(bit32.lshift(result, 8), bytes[i]) + end + end + + return result + end +end + +local function readUnicodeChar(self) + local byteArray = {string.byte(readString(self, 1))} + + local nullBitPosition = 0 + for i = 1, 7 do + if bit32.band(bit32.rshift(byteArray[1], 8 - i), 0x1) == 0x0 then + nullBitPosition = i + break + end + end + + for i = 1, nullBitPosition - 2 do + table.insert(byteArray, string.byte(readString(self, 1))) + end + + return string.char(table.unpack(byteArray)) +end + +local function read(self, format, ...) + local formatType = type(format) + if formatType == "number" then + return readChar(self, format) + elseif formatType == "string" then + format = format:gsub("^%*", "") + + if format == "a" then + return readAll(self) + elseif format == "l" then + return readLine(self) + elseif format == "b" then + return readByte(self) + elseif format == "bs" then + return readBytes(self, ...) + elseif format == "u" then + return readUnicodeChar(self) + else + error("bad argument #2 ('a' (whole file), 'l' (line), 'u' (unicode char), 'b' (byte as number) or 'bs' (sequence of n bytes as number) expected, got " .. format .. ")") + end + else + error("bad argument #1 (number or string expected, got " .. formatType ..")") + end +end + +local function seek(self, pizda, cyka) + if pizda == "set" then + local value = self.proxy.seek(self.stream, "set", -self.position + cyka) + self.position = cyka + self.buffer = "" + + return value + elseif pizda == "cur" then + local value = self.proxy.seek(self.stream, "cur", cyka) + self.position = self.position + cyka + self.buffer = "" + + return value + elseif pizda == "end" then + local value = self.proxy.seek(self.stream, "set", self.size - 1) + self.position = self.size - 1 + self.buffer = "" + + return value + else + error("bad argument #2 ('set', 'cur' or 'end' expected, got " .. tostring(whence) .. ")") + end +end + +local function write(self, ...) + local data = {...} + for i = 1, #data do + data[i] = tostring(data[i]) + end + data = table.concat(data) + + -- Data is small enough to fit buffer + if #data < (BUFFER_SIZE - #self.buffer) then + self.buffer = self.buffer .. data + + return true + else + -- Write current buffer content + local success, reason = self.proxy.write(self.stream, self.buffer) + if success then + -- If data will not fit buffer, use iterative writing with data partitioning + if #data > BUFFER_SIZE then + for i = 1, #data, BUFFER_SIZE do + success, reason = self.proxy.write(self.stream, data:sub(i, i + BUFFER_SIZE - 1)) + + if not success then + break + end + end + + self.buffer = "" + + return success, reason + -- Data will perfectly fit in empty buffer + else + self.buffer = data + + return true + end + else + return false, reason + end + end +end + +local function writeBytes(self, ...) + return write(self, string.char(...)) +end + +local function close(self) + if self.write and #self.buffer > 0 then + self.proxy.write(self.stream, self.buffer) + end + + return self.proxy.close(self.stream) +end + +function filesystem.open(path, mode) + local proxy, proxyPath = filesystem.get(path) + local result, reason = proxy.open(proxyPath, mode) + if result then + local handle = { + proxy = proxy, + stream = result, + position = 0, + buffer = "", + close = close, + seek = seek, + } + + if mode == "r" or mode == "rb" then + handle.size = proxy.size(proxyPath) + handle.readString = readString + handle.readUnicodeChar = readUnicodeChar + handle.readBytes = readBytes + handle.readLine = readLine + handle.lines = lines + handle.readAll = readAll + handle.read = read + + return handle + elseif mode == "w" or mode == "wb" or mode == "a" or mode == "ab" then + handle.write = write + handle.writeBytes = writeBytes + + return handle + else + error("bad argument #2 ('r', 'rb', 'w', 'wb' or 'a' expected, got )" .. tostring(mode) .. ")") + end + else + return nil, reason + end +end + +--------------------------------------- Rest proxy methods ----------------------------------------- + +function filesystem.exists(path) + local proxy, proxyPath = filesystem.get(path) + return proxy.exists(proxyPath) +end + +function filesystem.size(path) + local proxy, proxyPath = filesystem.get(path) + return proxy.size(proxyPath) +end + +function filesystem.isDirectory(path) + local proxy, proxyPath = filesystem.get(path) + return proxy.isDirectory(proxyPath) +end + +function filesystem.makeDirectory(path) + local proxy, proxyPath = filesystem.get(path) + return proxy.makeDirectory(proxyPath) +end + +function filesystem.lastModified(path) + local proxy, proxyPath = filesystem.get(path) + return proxy.lastModified(proxyPath) +end + +function filesystem.remove(path) + local proxy, proxyPath = filesystem.get(path) + return proxy.remove(proxyPath) +end + +function filesystem.rename(path, newPath) + local proxy, proxyPath = filesystem.get(path) + return proxy.rename(proxyPath, newPath) +end + +function filesystem.list(path, sortingMethod) + local proxy, proxyPath = filesystem.get(path) + + local list, reason = proxy.list(proxyPath) + if list then + -- Fullfill list with mounted paths if needed + for i = 1, #mountedProxies do + if path == filesystem.path(mountedProxies[i].path) then + table.insert(list, filesystem.name(mountedProxies[i].path) .. "/") + end + end + + -- Applying sorting methods + if not sortingMethod or sortingMethod == filesystem.SORTING_NAME then + table.sort(list, function(a, b) + return unicode.lower(a) < unicode.lower(b) + end) + + return list + elseif sortingMethod == filesystem.SORTING_DATE then + table.sort(list, function(a, b) + return filesystem.lastModified(path .. a) > filesystem.lastModified(path .. b) + end) + + return list + elseif sortingMethod == filesystem.SORTING_TYPE then + -- Creating a map with "extension" = {file1, file2, ...} structure + local map, extension = {} + for i = 1, #list do + extension = filesystem.extension(list[i]) or "Z" + + -- If it's a directory without extension + if extension:sub(1, 1) ~= "." and filesystem.isDirectory(path .. list[i]) then + extension = "." + end + + map[extension] = map[extension] or {} + table.insert(map[extension], list[i]) + end + + -- Sorting lists for each extension + local extensions = {} + for key, value in pairs(map) do + table.sort(value, function(a, b) + return unicode.lower(a) < unicode.lower(b) + end) + + table.insert(extensions, key) + end + + -- Sorting extensions + table.sort(extensions, function(a, b) + return unicode.lower(a) < unicode.lower(b) + end) + + -- Fullfilling final list + list = {} + for i = 1, #extensions do + for j = 1, #map[extensions[i]] do + table.insert(list, map[extensions[i]][j]) + end + end + + return list + end + end + + return list, reason +end + +--------------------------------------- Advanced methods ----------------------------------------- + +function filesystem.copy(from, to) + local fromHandle, reason = filesystem.open(from, "rb") + if fromHandle then + local toHandle, reason = filesystem.open(to, "wb") + if toHandle then + while true do + local chunk = readString(fromHandle, BUFFER_SIZE) + if chunk then + local success, reason = write(toHandle, chunk) + + if not success then + return false, reason + end + else + toHandle:close() + fromHandle:close() + + return true + end + end + else + return false, reason + end + else + return false, reason + end +end + +function filesystem.read(path) + local handle, reason = filesystem.open(path, "rb") + if handle then + local data = readAll(handle) + handle:close() + + return data + end + + return false, reason +end + +function filesystem.lines(path) + local handle, reason = filesystem.open(path, "rb") + if handle then + return handle:lines() + else + error(reason) + end +end + +function filesystem.readLines(path) + local handle, reason = filesystem.open(path, "rb") + if handle then + local lines, index, line = {}, 1 + + repeat + line = readLine(handle) + lines[index] = line + index = index + 1 + until not line + + handle:close() + + return lines + end + + return false, reason +end + +local function writeOrAppend(append, path, ...) + filesystem.makeDirectory(filesystem.path(path)) + + local handle, reason = filesystem.open(path, append and "ab" or "wb") + if handle then + local result, reason = write(handle, ...) + handle:close() + + return result, reason + end + + return false, reason +end + +function filesystem.write(path, ...) + return writeOrAppend(false, path,...) +end + +function filesystem.append(path, ...) + return writeOrAppend(true, path, ...) +end + +function filesystem.writeTable(path, ...) + return filesystem.write(path, require("Text").serialize(...)) +end + +function filesystem.readTable(path) + local result, reason = filesystem.read(path) + if result then + return require("Text").deserialize(result) + end + + return result, reason +end + +function filesystem.setProxy(proxy) + BOOT_PROXY = proxy +end + +function filesystem.getProxy() + return BOOT_PROXY +end + +--------------------------------------- loadfile() and dofile() implementation ----------------------------------------- + +function loadfile(path) + local data, reason = filesystem.read(path) + if data then + return load(data, "=" .. path) + end + + return nil, reason +end + +function dofile(path, ...) + local result, reason = loadfile(path) + if result then + local data = {xpcall(result, debug.traceback, ...)} + if data[1] then + return table.unpack(data, 2) + else + error(data[2]) + end + else + error(reason) + end +end + +-------------------------------------------------------------------------------- + +-- Mount all existing filesystem components +for address in component.list("filesystem") do + filesystem.mount(component.proxy(address), paths.system.mounts .. address .. "/") +end + +-- Automatically mount/unmount filesystem components +event.addHandler(function(signal, address, type) + if signal == "component_added" and type == "filesystem" then + filesystem.mount(component.proxy(address), paths.system.mounts .. address .. "/") + elseif signal == "component_removed" and type == "filesystem" then + filesystem.unmount(address) + end +end) + +-------------------------------------------------------------------------------- + +return filesystem diff --git a/lib/FormatModules/OCAF.lua b/Libraries/FormatModules/OCAF.lua similarity index 98% rename from lib/FormatModules/OCAF.lua rename to Libraries/FormatModules/OCAF.lua index faad2cb9..00530f05 100755 --- a/lib/FormatModules/OCAF.lua +++ b/Libraries/FormatModules/OCAF.lua @@ -1,8 +1,8 @@ require("advancedLua") -local bit32 = require("bit32") -local unicode = require("unicode") -local fs = require("filesystem") +local bit32 = require("Bit32") +local unicode = require("Unicode") +local fs = require("Filesystem") ----------------------------------------------------------------------------------------------- diff --git a/Libraries/GUI.lua b/Libraries/GUI.lua new file mode 100755 index 00000000..9cadc713 --- /dev/null +++ b/Libraries/GUI.lua @@ -0,0 +1,4442 @@ + +local keyboard = require("Keyboard") +local filesystem = require("Filesystem") +local event = require("Event") +local color = require("Color") +local image = require("Image") +local screen = require("Screen") +local paths = require("Paths") +local text = require("Text") +local number = require("Number") + +----------------------------------------------------------------------------------------- + +local GUI = { + ALIGNMENT_HORIZONTAL_LEFT = 1, + ALIGNMENT_HORIZONTAL_CENTER = 2, + ALIGNMENT_HORIZONTAL_RIGHT = 3, + ALIGNMENT_VERTICAL_TOP = 4, + ALIGNMENT_VERTICAL_CENTER = 5, + ALIGNMENT_VERTICAL_BOTTOM = 6, + + DIRECTION_HORIZONTAL = 7, + DIRECTION_VERTICAL = 8, + + SIZE_POLICY_ABSOLUTE = 9, + SIZE_POLICY_RELATIVE = 10, + + IO_MODE_FILE = 11, + IO_MODE_DIRECTORY = 12, + IO_MODE_BOTH = 13, + IO_MODE_OPEN = 14, + IO_MODE_SAVE = 15, + + BUTTON_PRESS_DURATION = 0.2, + BUTTON_ANIMATION_DURATION = 0.2, + SWITCH_ANIMATION_DURATION = 0.3, + FILESYSTEM_DIALOG_ANIMATION_DURATION = 0.5, + + CONTEXT_MENU_SEPARATOR_COLOR = 0xA5A5A5, + CONTEXT_MENU_DEFAULT_TEXT_COLOR = 0x2D2D2D, + CONTEXT_MENU_DEFAULT_BACKGROUND_COLOR = 0xFFFFFF, + CONTEXT_MENU_PRESSED_BACKGROUND_COLOR = 0x3366CC, + CONTEXT_MENU_PRESSED_TEXT_COLOR = 0xFFFFFF, + CONTEXT_MENU_DISABLED_COLOR = 0x878787, + CONTEXT_MENU_BACKGROUND_TRANSPARENCY = 0.18, + CONTEXT_MENU_SHADOW_TRANSPARENCY = 0.4, + + BACKGROUND_CONTAINER_PANEL_COLOR = 0x0, + BACKGROUND_CONTAINER_TITLE_COLOR = 0xE1E1E1, + BACKGROUND_CONTAINER_PANEL_TRANSPARENCY = 0.3, + + WINDOW_BACKGROUND_PANEL_COLOR = 0xF0F0F0, + WINDOW_SHADOW_TRANSPARENCY = 0.6, + WINDOW_TITLE_BACKGROUND_COLOR = 0xE1E1E1, + WINDOW_TITLE_TEXT_COLOR = 0x2D2D2D, + WINDOW_TAB_BAR_DEFAULT_BACKGROUND_COLOR = 0x2D2D2D, + WINDOW_TAB_BAR_DEFAULT_TEXT_COLOR = 0xF0F0F0, + WINDOW_TAB_BAR_SELECTED_BACKGROUND_COLOR = 0xF0F0F0, + WINDOW_TAB_BAR_SELECTED_TEXT_COLOR = 0x2D2D2D, + + PALETTE_CONFIG_PATH = paths.system.libraries .. ".palette.cfg", + + LUA_SYNTAX_COLOR_SCHEME = { + background = 0x1E1E1E, + text = 0xE1E1E1, + strings = 0x99FF80, + loops = 0xFFFF98, + comments = 0x898989, + boolean = 0xFFDB40, + logic = 0xFFCC66, + numbers = 0x66DBFF, + functions = 0xFFCC66, + compares = 0xFFCC66, + lineNumbersBackground = 0x2D2D2D, + lineNumbersText = 0xC3C3C3, + scrollBarBackground = 0x2D2D2D, + scrollBarForeground = 0x5A5A5A, + selection = 0x4B4B4B, + indentation = 0x2D2D2D + }, + + LUA_SYNTAX_PATTERNS = { + "[%.%,%>%<%=%~%+%-%*%/%^%#%%%&]", "compares", 0, 0, + "[^%a%d][%.%d]+[^%a%d]", "numbers", 1, 1, + "[^%a%d][%.%d]+$", "numbers", 1, 0, + "0x%w+", "numbers", 0, 0, + " not ", "logic", 0, 1, + " or ", "logic", 0, 1, + " and ", "logic", 0, 1, + "function%(", "functions", 0, 1, + "function%s[^%s%(%)%{%}%[%]]+%(", "functions", 9, 1, + "nil", "boolean", 0, 0, + "false", "boolean", 0, 0, + "true", "boolean", 0, 0, + " break$", "loops", 0, 0, + "elseif ", "loops", 0, 1, + "else[%s%;]", "loops", 0, 1, + "else$", "loops", 0, 0, + "function ", "loops", 0, 1, + "local ", "loops", 0, 1, + "return", "loops", 0, 0, + "until ", "loops", 0, 1, + "then", "loops", 0, 0, + "if ", "loops", 0, 1, + "repeat$", "loops", 0, 0, + " in ", "loops", 0, 1, + "for ", "loops", 0, 1, + "end[%s%;]", "loops", 0, 1, + "end$", "loops", 0, 0, + "do ", "loops", 0, 1, + "do$", "loops", 0, 0, + "while ", "loops", 0, 1, + "\'[^\']+\'", "strings", 0, 0, + "\"[^\"]+\"", "strings", 0, 0, + "%-%-.+", "comments", 0, 0, + }, +} + +-------------------------------------------------------------------------------- + +function GUI.setAlignment(object, horizontalAlignment, verticalAlignment) + object.horizontalAlignment, object.verticalAlignment = horizontalAlignment, verticalAlignment + + return object +end + +function GUI.getAlignmentCoordinates(x, y, width1, height1, horizontalAlignment, verticalAlignment, width2, height2) + if horizontalAlignment == GUI.ALIGNMENT_HORIZONTAL_CENTER then + x = x + width1 / 2 - width2 / 2 + elseif horizontalAlignment == GUI.ALIGNMENT_HORIZONTAL_RIGHT then + x = x + width1 - width2 + elseif horizontalAlignment ~= GUI.ALIGNMENT_HORIZONTAL_LEFT then + error("Unknown horizontal alignment: " .. tostring(horizontalAlignment)) + end + + if verticalAlignment == GUI.ALIGNMENT_VERTICAL_CENTER then + y = y + height1 / 2 - height2 / 2 + elseif verticalAlignment == GUI.ALIGNMENT_VERTICAL_BOTTOM then + y = y + height1 - height2 + elseif verticalAlignment ~= GUI.ALIGNMENT_VERTICAL_TOP then + error("Unknown vertical alignment: " .. tostring(verticalAlignment)) + end + + return x, y +end + +function GUI.getMarginCoordinates(x, y, horizontalAlignment, verticalAlignment, horizontalMargin, verticalMargin) + if horizontalAlignment == GUI.ALIGNMENT_HORIZONTAL_RIGHT then + x = x - horizontalMargin + else + x = x + horizontalMargin + end + + if verticalAlignment == GUI.ALIGNMENT_VERTICAL_BOTTOM then + y = y - verticalMargin + else + y = y + verticalMargin + end + + return x, y +end + +-------------------------------------------------------------------------------- + +local function objectIsPointInside(object, x, y) + return + x >= object.x and + x < object.x + object.width and + y >= object.y and + y < object.y + object.height +end + +local function objectDraw(object) + return object +end + +function GUI.object(x, y, width, height) + return { + x = x, + y = y, + width = width, + height = height, + isPointInside = objectIsPointInside, + draw = objectDraw + } +end + +-------------------------------------------------------------------------------- + +local function containerObjectIndexOf(object) + if not object.parent then error("Object doesn't have a parent container") end + + for objectIndex = 1, #object.parent.children do + if object.parent.children[objectIndex] == object then + return objectIndex + end + end +end + +local function containerObjectMoveForward(object) + local objectIndex = containerObjectIndexOf(object) + if objectIndex < #object.parent.children then + object.parent.children[index], object.parent.children[index + 1] = object.parent.children[index + 1], object.parent.children[index] + end + + return object +end + +local function containerObjectMoveBackward(object) + local objectIndex = containerObjectIndexOf(object) + if objectIndex > 1 then + object.parent.children[objectIndex], object.parent.children[objectIndex - 1] = object.parent.children[objectIndex - 1], object.parent.children[objectIndex] + end + + return object +end + +local function containerObjectMoveToFront(object) + table.remove(object.parent.children, containerObjectIndexOf(object)) + table.insert(object.parent.children, object) + + return object +end + +local function containerObjectMoveToBack(object) + table.remove(object.parent.children, containerObjectIndexOf(object)) + table.insert(object.parent.children, 1, object) + + return object +end + +local function containerObjectRemove(object) + table.remove(object.parent.children, containerObjectIndexOf(object)) +end + +local function containerObjectAnimationStart(animation, duration) + animation.position = 0 + animation.duration = duration + animation.started = true + animation.startUptime = computer.uptime() + + computer.pushSignal("GUI", "animationStarted") +end + +local function containerObjectAnimationStop(animation) + animation.position = 0 + animation.started = false +end + +local function containerObjectAnimationRemove(animation) + animation.removeLater = true +end + +local function containerObjectAddAnimation(object, frameHandler, onFinish) + local animation = { + object = object, + position = 0, + start = containerObjectAnimationStart, + stop = containerObjectAnimationStop, + remove = containerObjectAnimationRemove, + frameHandler = frameHandler, + onFinish = onFinish, + } + + object.firstParent.animations = object.firstParent.animations or {} + table.insert(object.firstParent.animations, animation) + + return animation +end + +local function containerAddChild(container, object, atIndex) + object.localX = object.x + object.localY = object.y + object.indexOf = containerObjectIndexOf + object.moveToFront = containerObjectMoveToFront + object.moveToBack = containerObjectMoveToBack + object.moveForward = containerObjectMoveForward + object.moveBackward = containerObjectMoveBackward + object.remove = containerObjectRemove + object.addAnimation = containerObjectAddAnimation + + local function updateFirstParent(object, firstParent) + object.firstParent = firstParent + if object.children then + for i = 1, #object.children do + updateFirstParent(object.children[i], firstParent) + end + end + end + + object.parent = container + updateFirstParent(object, container.firstParent or container) + + if atIndex then + table.insert(container.children, atIndex, object) + else + table.insert(container.children, object) + end + + return object +end + +local function containerRemoveChildren(container, from, to) + from = from or 1 + for objectIndex = from, to or #container.children do + table.remove(container.children, from) + end +end + +local function getRectangleIntersection(R1X1, R1Y1, R1X2, R1Y2, R2X1, R2Y1, R2X2, R2Y2) + if R2X1 <= R1X2 and R2Y1 <= R1Y2 and R2X2 >= R1X1 and R2Y2 >= R1Y1 then + return + math.max(R2X1, R1X1), + math.max(R2Y1, R1Y1), + math.min(R2X2, R1X2), + math.min(R2Y2, R1Y2) + else + return + end +end + +local function containerDraw(container) + local R1X1, R1Y1, R1X2, R1Y2, child = screen.getDrawLimit() + local intersectionX1, intersectionY1, intersectionX2, intersectionY2 = getRectangleIntersection( + R1X1, + R1Y1, + R1X2, + R1Y2, + container.x, + container.y, + container.x + container.width - 1, + container.y + container.height - 1 + ) + + if intersectionX1 then + screen.setDrawLimit(intersectionX1, intersectionY1, intersectionX2, intersectionY2) + + for i = 1, #container.children do + child = container.children[i] + + if not child.hidden then + child.x, child.y = container.x + child.localX - 1, container.y + child.localY - 1 + child:draw() + end + end + + screen.setDrawLimit(R1X1, R1Y1, R1X2, R1Y2) + end + + return container +end + +function GUI.container(x, y, width, height) + local container = GUI.object(x, y, width, height) + + container.children = {} + container.passScreenEvents = true + + container.draw = containerDraw + container.removeChildren = containerRemoveChildren + container.addChild = containerAddChild + + return container +end + +-------------------------------------------------------------------------------- + +local function workspaceStart(workspace, eventPullTimeout) + local animation, animationIndex, animationOnFinishMethodsIndex, animationOnFinishMethods, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32 + + local function handle(isScreenEvent, currentContainer, intersectionX1, intersectionY1, intersectionX2, intersectionY2) + if + not isScreenEvent or + intersectionX1 and + e3 >= intersectionX1 and + e3 <= intersectionX2 and + e4 >= intersectionY1 and + e4 <= intersectionY2 + then + local currentContainerPassed, child, newIntersectionX1, newIntersectionY1, newIntersectionX2, newIntersectionY2 + + if isScreenEvent then + if currentContainer.eventHandler and not currentContainer.disabled then + currentContainer.eventHandler(workspace, currentContainer, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32) + end + + currentContainerPassed = not currentContainer.passScreenEvents + elseif currentContainer.eventHandler then + currentContainer.eventHandler(workspace, currentContainer, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32) + end + + for i = #currentContainer.children, 1, -1 do + child = currentContainer.children[i] + + if not child.hidden then + if child.children then + newIntersectionX1, newIntersectionY1, newIntersectionX2, newIntersectionY2 = getRectangleIntersection( + intersectionX1, + intersectionY1, + intersectionX2, + intersectionY2, + child.x, + child.y, + child.x + child.width - 1, + child.y + child.height - 1 + ) + + if + newIntersectionX1 and + handle( + isScreenEvent, + child, + newIntersectionX1, + newIntersectionY1, + newIntersectionX2, + newIntersectionY2, + e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32 + ) + then + return true + end + else + if workspace.needConsume then + workspace.needConsume = nil + return true + end + + if isScreenEvent then + if child:isPointInside(e3, e4) then + if child.eventHandler and not child.disabled then + child.eventHandler(workspace, child, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32) + end + + if not child.passScreenEvents then + return true + end + end + elseif child.eventHandler then + child.eventHandler(workspace, child, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32) + end + end + end + end + + if currentContainerPassed then + return true + end + end + end + + workspace.eventPullTimeout = eventPullTimeout + + repeat + e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32 = event.pull(workspace.animations and 0 or workspace.eventPullTimeout) + + handle( + e1 == "touch" or + e1 == "drag" or + e1 == "drop" or + e1 == "scroll" or + e1 == "double_touch", + workspace, + workspace.x, + workspace.y, + workspace.x + workspace.width - 1, + workspace.y + workspace.height - 1 + ) + + if workspace.animations then + animationIndex, animationOnFinishMethodsIndex, animationOnFinishMethods = 1, 1, {} + -- Продрачиваем анимации и вызываем обработчики кадров + while animationIndex <= #workspace.animations do + animation = workspace.animations[animationIndex] + + if animation.removeLater then + table.remove(workspace.animations, animationIndex) + + if #workspace.animations == 0 then + workspace.animations = nil + break + end + else + if animation.started then + animation.position = (computer.uptime() - animation.startUptime) / animation.duration + + if animation.position < 1 then + animation.frameHandler(animation) + else + animation.position, animation.started = 1, false + animation.frameHandler(animation) + + animationOnFinishMethods[animationOnFinishMethodsIndex] = animation + animationOnFinishMethodsIndex = animationOnFinishMethodsIndex + 1 + end + end + + animationIndex = animationIndex + 1 + end + end + + -- По завершению продрочки отрисовываем изменения на экране + workspace:draw() + + -- Вызываем поочередно все методы .onFinish + for i = 1, #animationOnFinishMethods do + animationOnFinishMethods[i].onFinish(animationOnFinishMethods[i]) + end + end + until workspace.needClose + + workspace.needClose = nil +end + +local function workspaceStop(workspace) + workspace.needClose = true +end + +local function workspaceConsumeEvent(workspace) + workspace.needConsume = true +end + +local function workspaceDraw(object, ...) + containerDraw(object) + screen.update(...) +end + +function GUI.workspace(x, y, width, height) + local workspace = GUI.container(x or 1, y or 1, width or screen.getWidth(), height or screen.getHeight()) + + workspace.draw = workspaceDraw + workspace.start = workspaceStart + workspace.stop = workspaceStop + workspace.consumeEvent = workspaceConsumeEvent + + return workspace +end + +-------------------------------------------------------------------------------- + +local function pressableDraw(pressable) + local background = pressable.pressed and pressable.colors.pressed.background or pressable.disabled and pressable.colors.disabled.background or pressable.colors.default.background + local text = pressable.pressed and pressable.colors.pressed.text or pressable.disabled and pressable.colors.disabled.text or pressable.colors.default.text + + if background then + screen.drawRectangle(pressable.x, pressable.y, pressable.width, pressable.height, background, text, " ") + end + screen.drawText(math.floor(pressable.x + pressable.width / 2 - unicode.len(pressable.text) / 2), math.floor(pressable.y + pressable.height / 2), text, pressable.text) +end + +local function pressableHandlePress(workspace, pressable, ...) + pressable.pressed = not pressable.pressed + workspace:draw() + + if not pressable.switchMode then + pressable.pressed = not pressable.pressed + event.sleep(GUI.BUTTON_PRESS_DURATION) + + workspace:draw() + end + + if pressable.onTouch then + pressable.onTouch(workspace, pressable, ...) + end +end + +local function pressableEventHandler(workspace, pressable, e1, ...) + if e1 == "touch" then + pressableHandlePress(workspace, pressable, e1, ...) + end +end + +local function pressable(x, y, width, height, backgroundColor, textColor, backgroundPressedColor, textPressedColor, backgroundDisabledColor, textDisabledColor, text) + local pressable = GUI.object(x, y, width, height) + + pressable.colors = { + default = { + background = backgroundColor, + text = textColor + }, + pressed = { + background = backgroundPressedColor, + text = textPressedColor + }, + disabled = { + background = backgroundDisabledColor, + text = textDisabledColor + } + } + + pressable.pressed = false + pressable.text = text + pressable.draw = pressableDraw + pressable.eventHandler = pressableEventHandler + + return pressable +end + +-------------------------------------------------------------------------------- + +local function buttonPlayAnimation(button, onFinish) + button.animationStarted = true + button:addAnimation( + function(animation) + if button.pressed then + if button.colors.default.background and button.colors.pressed.background then + button.animationCurrentBackground = color.transition(button.colors.pressed.background, button.colors.default.background, animation.position) + end + button.animationCurrentText = color.transition(button.colors.pressed.text, button.colors.default.text, animation.position) + else + if button.colors.default.background and button.colors.pressed.background then + button.animationCurrentBackground = color.transition(button.colors.default.background, button.colors.pressed.background, animation.position) + end + button.animationCurrentText = color.transition(button.colors.default.text, button.colors.pressed.text, animation.position) + end + end, + function(animation) + button.animationStarted = false + button.pressed = not button.pressed + onFinish(animation) + end + ):start(button.animationDuration) +end + +local function buttonPress(button, workspace, object, ...) + if button.animated then + local eventData = {...} + + buttonPlayAnimation(button, function(animation) + if button.onTouch then + button.onTouch(workspace, button, table.unpack(eventData)) + end + + animation:remove() + + if not button.switchMode then + buttonPlayAnimation(button, function(animation) + animation:remove() + end) + end + end) + else + pressableHandlePress(workspace, button, ...) + end +end + +local function buttonEventHandler(workspace, button, e1, ...) + if e1 == "touch" and (not button.animated or not button.animationStarted) then + button:press(workspace, button, e1, ...) + end +end + +local function buttonGetColors(button) + if button.disabled then + return button.colors.disabled.background, button.colors.disabled.text + else + if button.animated and button.animationStarted then + return button.animationCurrentBackground, button.animationCurrentText + else + if button.pressed then + return button.colors.pressed.background, button.colors.pressed.text + else + return button.colors.default.background, button.colors.default.text + end + end + end +end + +local function buttonDrawText(button, textColor) + screen.drawText(math.floor(button.x + button.width / 2 - unicode.len(button.text) / 2), math.floor(button.y + button.height / 2), textColor, button.text) +end + +local function buttonDraw(button) + local backgroundColor, textColor = buttonGetColors(button) + + if backgroundColor then + screen.drawRectangle(button.x, button.y, button.width, button.height, backgroundColor, textColor, " ", button.colors.transparency) + end + buttonDrawText(button, textColor) +end + +local function framedButtonDraw(button) + local backgroundColor, textColor = buttonGetColors(button) + + if backgroundColor then + screen.drawFrame(button.x, button.y, button.width, button.height, backgroundColor) + end + buttonDrawText(button, textColor) +end + +local function roundedButtonDraw(button) + local backgroundColor, textColor = buttonGetColors(button) + + if backgroundColor then + local x2, y2 = button.x + button.width - 1, button.y + button.height - 1 + if button.height > 1 then + screen.drawText(button.x + 1, button.y, backgroundColor, string.rep("▄", button.width - 2)) + screen.drawText(button.x, button.y, backgroundColor, "⣠") + screen.drawText(x2, button.y, backgroundColor, "⣄") + + screen.drawRectangle(button.x, button.y + 1, button.width, button.height - 2, backgroundColor, textColor, " ") + + screen.drawText(button.x + 1, y2, backgroundColor, string.rep("▀", button.width - 2)) + screen.drawText(button.x, y2, backgroundColor, "⠙") + screen.drawText(x2, y2, backgroundColor, "⠋") + else + screen.drawRectangle(button.x, button.y, button.width, button.height, backgroundColor, textColor, " ") + GUI.roundedCorners(button.x, button.y, button.width, button.height, backgroundColor) + end + end + + buttonDrawText(button, textColor) +end + +local function tagButtonDraw(button) + local backgroundColor, textColor = buttonGetColors(button) + + screen.drawRectangle(button.x, button.y, button.width, button.height, backgroundColor, textColor, " ") + screen.drawText(button.x - 1, button.y, backgroundColor, "◀") + buttonDrawText(button, textColor) +end + +local function buttonCreate(x, y, width, height, backgroundColor, textColor, backgroundPressedColor, textPressedColor, text) + local button = pressable(x, y, width, height, backgroundColor, textColor, backgroundPressedColor, textPressedColor, 0x878787, 0xA5A5A5, text) + + button.animationDuration = GUI.BUTTON_ANIMATION_DURATION + button.animated = true + + button.animationCurrentBackground = backgroundColor + button.animationCurrentText = textColor + + button.press = buttonPress + button.eventHandler = buttonEventHandler + + return button +end + +local function adaptiveButtonCreate(x, y, xOffset, yOffset, backgroundColor, textColor, backgroundPressedColor, textPressedColor, text) + return buttonCreate(x, y, unicode.len(text) + xOffset * 2, yOffset * 2 + 1, backgroundColor, textColor, backgroundPressedColor, textPressedColor, text) +end + +function GUI.button(...) + local button = buttonCreate(...) + button.draw = buttonDraw + + return button +end + +function GUI.adaptiveButton(...) + local button = adaptiveButtonCreate(...) + button.draw = buttonDraw + + return button +end + +function GUI.framedButton(...) + local button = buttonCreate(...) + button.draw = framedButtonDraw + + return button +end + +function GUI.adaptiveFramedButton(...) + local button = adaptiveButtonCreate(...) + button.draw = framedButtonDraw + + return button +end + +function GUI.roundedButton(...) + local button = buttonCreate(...) + button.draw = roundedButtonDraw + + return button +end + +function GUI.adaptiveRoundedButton(...) + local button = adaptiveButtonCreate(...) + button.draw = roundedButtonDraw + + return button +end + +function GUI.tagButton(...) + local button = buttonCreate(...) + button.draw = tagButtonDraw + + return button +end + +function GUI.adaptiveTagButton(...) + local button = adaptiveButtonCreate(...) + button.draw = tagButtonDraw + + return button +end + +-------------------------------------------------------------------------------- + +local function drawPanel(object) + screen.drawRectangle(object.x, object.y, object.width, object.height, object.colors.background, 0x0, " ", object.colors.transparency) + return object +end + +function GUI.panel(x, y, width, height, color, transparency) + local object = GUI.object(x, y, width, height) + + object.colors = { + background = color, + transparency = transparency + } + object.draw = drawPanel + + return object +end + +-------------------------------------------------------------------------------- + +local function drawLabel(object) + local xText, yText = GUI.getAlignmentCoordinates( + object.x, + object.y, + object.width, + object.height, + object.horizontalAlignment, + object.verticalAlignment, + unicode.len(object.text), + 1 + ) + screen.drawText(math.floor(xText), math.floor(yText), object.colors.text, object.text) + return object +end + +function GUI.label(x, y, width, height, textColor, text) + local object = GUI.object(x, y, width, height) + + object.setAlignment = GUI.setAlignment + object:setAlignment(GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP) + object.colors = {text = textColor} + object.text = text + object.draw = drawLabel + + return object +end + +-------------------------------------------------------------------------------- + +local function drawImage(object) + screen.drawImage(object.x, object.y, object.image) + return object +end + +function GUI.image(x, y, image) + local object = GUI.object(x, y, image[1], image[2]) + + object.image = image + object.draw = drawImage + + return object +end + +-------------------------------------------------------------------------------- + +function GUI.actionButtons(x, y, fatSymbol) + local symbol = fatSymbol and "⬤" or "●" + + local container = GUI.container(x, y, 5, 1) + container.close = container:addChild(GUI.button(1, 1, 1, 1, nil, 0xFF4940, nil, 0x992400, symbol)) + container.minimize = container:addChild(GUI.button(3, 1, 1, 1, nil, 0xFFB640, nil, 0x996D00, symbol)) + container.maximize = container:addChild(GUI.button(5, 1, 1, 1, nil, 0x00B640, nil, 0x006D40, symbol)) + + return container +end + +-------------------------------------------------------------------------------- + +local function drawProgressBar(object) + local activeWidth = math.floor(math.min(object.value, 100) / 100 * object.width) + if object.thin then + screen.drawText(object.x, object.y, object.colors.passive, string.rep("━", object.width)) + screen.drawText(object.x, object.y, object.colors.active, string.rep("━", activeWidth)) + else + screen.drawRectangle(object.x, object.y, object.width, object.height, object.colors.passive, 0x0, " ") + screen.drawRectangle(object.x, object.y, activeWidth, object.height, object.colors.active, 0x0, " ") + end + + if object.showValue then + local stringValue = (object.valuePrefix or "") .. object.value .. (object.valuePostfix or "") + screen.drawText(math.floor(object.x + object.width / 2 - unicode.len(stringValue) / 2), object.y + 1, object.colors.value, stringValue) + end + + return object +end + +function GUI.progressBar(x, y, width, activeColor, passiveColor, valueColor, value, thin, showValue, valuePrefix, valuePostfix) + local object = GUI.object(x, y, width, 1) + + object.value = value + object.colors = {active = activeColor, passive = passiveColor, value = valueColor} + object.thin = thin + object.draw = drawProgressBar + object.showValue = showValue + object.valuePrefix = valuePrefix + object.valuePostfix = valuePostfix + + return object +end + +-------------------------------------------------------------------------------- + +function GUI.drawShadow(x, y, width, height, transparency, thin) + if thin then + screen.drawRectangle(x + width, y + 1, 1, height - 1, 0x0, 0x0, " ", transparency) + screen.drawText(x + 1, y + height, 0x0, string.rep("▀", width), transparency) + screen.drawText(x + width, y, 0x0, "▄", transparency) + else + screen.drawRectangle(x + width, y + 1, 2, height, 0x0, 0x0, " ", transparency) + screen.drawRectangle(x + 2, y + height, width - 2, 1, 0x0, 0x0, " ", transparency) + end +end + +function GUI.roundedCorners(x, y, width, height, color, transparency) + screen.drawText(x - 1, y, color, "⠰", transparency) + screen.drawText(x + width, y, color, "⠆", transparency) +end + +-------------------------------------------------------------------------------- + +function GUI.alert(...) + local args = {...} + for i = 1, #args do + if type(args[i]) == "table" then + args[i] = text.serialize(args[i], true) + else + args[i] = tostring(args[i]) + end + end + if #args == 0 then args[1] = "nil" end + + local sign = image.fromString([[06030000FF 0000FF 00F7FF▟00F7FF▙0000FF 0000FF 0000FF 00F7FF▟F7FF00 F7FF00 00F7FF▙0000FF 00F7FF▟F7FF00CF7FF00yF7FF00kF7FF00a00F7FF▙]]) + local offset = 2 + local lines = #args > 1 and "\"" .. table.concat(args, "\", \"") .. "\"" or args[1] + local bufferWidth, bufferHeight = screen.getResolution() + local width = math.floor(bufferWidth * 0.5) + local textWidth = width - image.getWidth(sign) - 2 + + lines = text.wrap(lines, textWidth) + local height = image.getHeight(sign) + if #lines + 2 > height then + height = #lines + 2 + end + + local workspace = GUI.workspace(1, math.floor(bufferHeight / 2 - height / 2), bufferWidth, height + offset * 2) + local oldPixels = screen.copy(workspace.x, workspace.y, workspace.width, workspace.height) + + local x, y = math.floor(bufferWidth / 2 - width / 2), offset + 1 + workspace:addChild(GUI.panel(1, 1, workspace.width, workspace.height, 0x1D1D1D)) + workspace:addChild(GUI.image(x, y, sign)) + workspace:addChild(GUI.textBox(x + image.getWidth(sign) + 2, y, textWidth, #lines, 0x1D1D1D, 0xE1E1E1, lines, 1, 0, 0)).eventHandler = nil + local buttonWidth = 10 + local button = workspace:addChild(GUI.roundedButton(x + image.getWidth(sign) + textWidth - buttonWidth + 2, workspace.height - offset, buttonWidth, 1, 0x3366CC, 0xE1E1E1, 0xE1E1E1, 0x3366CC, "OK")) + + button.onTouch = function() + workspace:stop() + screen.paste(workspace.x, workspace.y, oldPixels) + screen.update() + end + + workspace.eventHandler = function(workspace, object, e1, e2, e3, e4, ...) + if e1 == "key_down" and e4 == 28 then + button.animated = false + button:press(workspace, object, e1, e2, e3, e4, ...) + end + end + + workspace:draw(true) + workspace:start() +end + +-------------------------------------------------------------------------------- + +local function codeViewDraw(codeView) + local toLine, colorScheme, patterns = codeView.fromLine + codeView.height - 1, codeView.syntaxColorScheme, codeView.syntaxPatterns + -- Line numbers bar and code area + codeView.lineNumbersWidth = unicode.len(tostring(toLine)) + 2 + codeView.codeAreaPosition = codeView.x + codeView.lineNumbersWidth + codeView.codeAreaWidth = codeView.width - codeView.lineNumbersWidth + -- Line numbers + screen.drawRectangle(codeView.x, codeView.y, codeView.lineNumbersWidth, codeView.height, colorScheme.lineNumbersBackground, colorScheme.lineNumbersText, " ") + -- Background + screen.drawRectangle(codeView.codeAreaPosition, codeView.y, codeView.codeAreaWidth, codeView.height, colorScheme.background, colorScheme.text, " ") + -- Line numbers texts + local y = codeView.y + for line = codeView.fromLine, toLine do + if codeView.lines[line] then + local text = tostring(line) + if codeView.highlights[line] then + screen.drawRectangle(codeView.x, y, codeView.lineNumbersWidth, 1, codeView.highlights[line], colorScheme.text, " ", 0.3) + screen.drawRectangle(codeView.codeAreaPosition, y, codeView.codeAreaWidth, 1, codeView.highlights[line], colorScheme.text, " ") + end + screen.drawText(codeView.codeAreaPosition - unicode.len(text) - 1, y, colorScheme.lineNumbersText, text) + y = y + 1 + else + break + end + end + + local function drawUpperSelection(y, selectionIndex) + screen.drawRectangle( + codeView.codeAreaPosition + codeView.selections[selectionIndex].from.symbol - codeView.fromSymbol + 1, + y + codeView.selections[selectionIndex].from.line - codeView.fromLine, + codeView.codeAreaWidth - codeView.selections[selectionIndex].from.symbol + codeView.fromSymbol - 1, + 1, + codeView.selections[selectionIndex].color or colorScheme.selection, colorScheme.text, " " + ) + end + + local function drawLowerSelection(y, selectionIndex) + screen.drawRectangle( + codeView.codeAreaPosition, + y + codeView.selections[selectionIndex].from.line - codeView.fromLine, + codeView.selections[selectionIndex].to.symbol - codeView.fromSymbol + 2, + 1, + codeView.selections[selectionIndex].color or colorScheme.selection, colorScheme.text, " " + ) + end + + local oldDrawLimitX1, oldDrawLimitY1, oldDrawLimitX2, oldDrawLimitY2 = screen.getDrawLimit() + screen.setDrawLimit(codeView.codeAreaPosition, codeView.y, codeView.codeAreaPosition + codeView.codeAreaWidth - 1, codeView.y + codeView.height - 1) + + if #codeView.selections > 0 then + for selectionIndex = 1, #codeView.selections do + y = codeView.y + local dy = codeView.selections[selectionIndex].to.line - codeView.selections[selectionIndex].from.line + if dy == 0 then + screen.drawRectangle( + codeView.codeAreaPosition + codeView.selections[selectionIndex].from.symbol - codeView.fromSymbol + 1, + y + codeView.selections[selectionIndex].from.line - codeView.fromLine, + codeView.selections[selectionIndex].to.symbol - codeView.selections[selectionIndex].from.symbol + 1, + 1, + codeView.selections[selectionIndex].color or colorScheme.selection, colorScheme.text, " " + ) + elseif dy == 1 then + drawUpperSelection(y, selectionIndex); y = y + 1 + drawLowerSelection(y, selectionIndex) + else + drawUpperSelection(y, selectionIndex); y = y + 1 + for i = 1, dy - 1 do + screen.drawRectangle(codeView.codeAreaPosition, y + codeView.selections[selectionIndex].from.line - codeView.fromLine, codeView.codeAreaWidth, 1, codeView.selections[selectionIndex].color or colorScheme.selection, colorScheme.text, " "); y = y + 1 + end + + drawLowerSelection(y, selectionIndex) + end + end + end + + -- Code strings + y = codeView.y + screen.setDrawLimit(codeView.codeAreaPosition + 1, y, codeView.codeAreaPosition + codeView.codeAreaWidth - 2, y + codeView.height - 1) + + for i = codeView.fromLine, toLine do + if codeView.lines[i] then + if codeView.syntaxHighlight then + GUI.highlightString(codeView.codeAreaPosition + 1, + y, + codeView.codeAreaWidth - 2, + codeView.fromSymbol, + codeView.indentationWidth, + patterns, + colorScheme, + codeView.lines[i] + ) + else + screen.drawText(codeView.codeAreaPosition - codeView.fromSymbol + 2, y, colorScheme.text, codeView.lines[i]) + end + + y = y + 1 + else + break + end + end + + screen.setDrawLimit(oldDrawLimitX1, oldDrawLimitY1, oldDrawLimitX2, oldDrawLimitY2) + + if #codeView.lines > codeView.height then + codeView.verticalScrollBar.colors.background, codeView.verticalScrollBar.colors.foreground = colorScheme.scrollBarBackground, colorScheme.scrollBarForeground + codeView.verticalScrollBar.minimumValue, codeView.verticalScrollBar.maximumValue, codeView.verticalScrollBar.value, codeView.verticalScrollBar.shownValueCount = 1, #codeView.lines, codeView.fromLine, codeView.height + codeView.verticalScrollBar.localX = codeView.width + codeView.verticalScrollBar.localY = 1 + codeView.verticalScrollBar.height = codeView.height - 1 + codeView.verticalScrollBar.hidden = false + else + codeView.verticalScrollBar.hidden = true + end + + if codeView.maximumLineLength > codeView.codeAreaWidth - 2 then + codeView.horizontalScrollBar.colors.background, codeView.horizontalScrollBar.colors.foreground = colorScheme.scrollBarBackground, colorScheme.scrollBarForeground + codeView.horizontalScrollBar.minimumValue, codeView.horizontalScrollBar.maximumValue, codeView.horizontalScrollBar.value, codeView.horizontalScrollBar.shownValueCount = 1, codeView.maximumLineLength, codeView.fromSymbol, codeView.codeAreaWidth - 2 + codeView.horizontalScrollBar.localX = codeView.lineNumbersWidth + 1 + codeView.horizontalScrollBar.localY = codeView.height + codeView.horizontalScrollBar.width = codeView.codeAreaWidth - 1 + codeView.horizontalScrollBar.hidden = false + else + codeView.horizontalScrollBar.hidden = true + end + + codeView:overrideDraw() +end + +function GUI.codeView(x, y, width, height, fromSymbol, fromLine, maximumLineLength, selections, highlights, syntaxPatterns, syntaxColorScheme, syntaxHighlight, lines) + local codeView = GUI.container(x, y, width, height) + + codeView.passScreenEvents = false + codeView.lines = lines + codeView.fromSymbol = fromSymbol + codeView.fromLine = fromLine + codeView.maximumLineLength = maximumLineLength + codeView.selections = selections or {} + codeView.highlights = highlights or {} + codeView.syntaxHighlight = syntaxHighlight + codeView.syntaxPatterns = syntaxPatterns + codeView.syntaxColorScheme = syntaxColorScheme + codeView.indentationWidth = 2 + + codeView.verticalScrollBar = codeView:addChild(GUI.scrollBar(1, 1, 1, 1, 0x0, 0x0, 1, 1, 1, 1, 1, true)) + codeView.horizontalScrollBar = codeView:addChild(GUI.scrollBar(1, 1, 1, 1, 0x0, 0x0, 1, 1, 1, 1, 1, true)) + + codeView.overrideDraw = codeView.draw + codeView.draw = codeViewDraw + + return codeView +end + +-------------------------------------------------------------------------------- + +local function colorSelectorDraw(colorSelector) + local overlayColor = colorSelector.color < 0x7FFFFF and 0xFFFFFF or 0x0 + + screen.drawRectangle( + colorSelector.x, + colorSelector.y, + colorSelector.width, + colorSelector.height, + colorSelector.pressed and color.blend(colorSelector.color, overlayColor, 0.8) or colorSelector.color, + overlayColor, + " " + ) + + if colorSelector.height > 1 and colorSelector.drawLine then + screen.drawText(colorSelector.x, colorSelector.y + colorSelector.height - 1, overlayColor, string.rep("▄", colorSelector.width), 0.8) + end + + screen.drawText(colorSelector.x + 1, colorSelector.y + math.floor(colorSelector.height / 2), overlayColor, text.limit(colorSelector.text, colorSelector.width - 2)) + + return colorSelector +end + +local function colorSelectorEventHandler(workspace, object, e1, ...) + if e1 == "touch" then + local eventData = {...} + object.pressed = true + + local palette = workspace:addChild(GUI.palette(1, 1, object.color)) + palette.localX, palette.localY = math.floor(workspace.width / 2 - palette.width / 2), math.floor(workspace.height / 2 - palette.height / 2) + + palette.cancelButton.onTouch = function() + object.pressed = false + palette:remove() + workspace:draw() + + if object.onColorSelected then + object.onColorSelected(workspace, object, e1, table.unpack(eventData)) + end + end + + palette.submitButton.onTouch = function() + object.color = palette.color.integer + palette.cancelButton.onTouch() + end + + workspace:draw() + end +end + +function GUI.colorSelector(x, y, width, height, color, text) + local colorSelector = GUI.object(x, y, width, height) + + colorSelector.drawLine = true + colorSelector.eventHandler = colorSelectorEventHandler + colorSelector.color = color + colorSelector.text = text + colorSelector.draw = colorSelectorDraw + + return colorSelector +end + +-------------------------------------------------------------------------------- + +local function getAxisValue(number, postfix, roundValues) + if roundValues then + return math.floor(number) .. postfix + else + local integer, fractional = math.modf(number) + local firstPart, secondPart = "", "" + if math.abs(integer) >= 1000 then + return number.shorten(integer, 2) .. postfix + else + if math.abs(fractional) > 0 then + return string.format("%.2f", number) .. postfix + else + return number .. postfix + end + end + end +end + +local function drawChart(object) + -- Sorting by x value + local valuesCopy = {} + for i = 1, #object.values do valuesCopy[i] = object.values[i] end + table.sort(valuesCopy, function(a, b) return a[1] < b[1] end) + + if #valuesCopy == 0 then valuesCopy = {{0, 0}} end + + -- Max, min, deltas + local xMin, xMax, yMin, yMax = valuesCopy[1][1], valuesCopy[#valuesCopy][1], valuesCopy[1][2], valuesCopy[1][2] + for i = 1, #valuesCopy do yMin, yMax = math.min(yMin, valuesCopy[i][2]), math.max(yMax, valuesCopy[i][2]) end + local dx, dy = xMax - xMin, yMax - yMin + + -- y axis values and helpers + local value, chartHeight, yAxisValueMaxWidth, yAxisValues = yMin, object.height - 1 - (object.showXAxisValues and 1 or 0), 0, {} + for y = object.y + object.height - 3, object.y + 1, -chartHeight * object.yAxisValueInterval do + local stringValue = getAxisValue(value, object.yAxisPostfix, object.roundValues) + yAxisValueMaxWidth = math.max(yAxisValueMaxWidth, unicode.len(stringValue)) + table.insert(yAxisValues, {y = math.ceil(y), value = stringValue}) + value = value + dy * object.yAxisValueInterval + end + local stringValue = getAxisValue(yMax, object.yAxisPostfix, object.roundValues) + table.insert(yAxisValues, {y = object.y, value = stringValue}) + yAxisValueMaxWidth = math.max(yAxisValueMaxWidth, unicode.len(stringValue)) + + local chartWidth = object.width - (object.showYAxisValues and yAxisValueMaxWidth + 2 or 0) + local chartX = object.x + object.width - chartWidth + for i = 1, #yAxisValues do + if object.showYAxisValues then + screen.drawText(chartX - unicode.len(yAxisValues[i].value) - 2, yAxisValues[i].y, object.colors.axisValue, yAxisValues[i].value) + end + screen.drawText(chartX, yAxisValues[i].y, object.colors.helpers, string.rep("─", chartWidth)) + end + + -- x axis values + if object.showXAxisValues then + value = xMin + for x = chartX, chartX + chartWidth - 2, chartWidth * object.xAxisValueInterval do + local stringValue = getAxisValue(value, object.xAxisPostfix, object.roundValues) + screen.drawText(math.floor(x - unicode.len(stringValue) / 2), object.y + object.height - 1, object.colors.axisValue, stringValue) + value = value + dx * object.xAxisValueInterval + end + local value = getAxisValue(xMax, object.xAxisPostfix, object.roundValues) + screen.drawText(object.x + object.width - unicode.len(value), object.y + object.height - 1, object.colors.axisValue, value) + end + + -- Axis lines + for y = object.y, object.y + chartHeight - 1 do + screen.drawText(chartX - 1, y, object.colors.axis, "┨") + end + screen.drawText(chartX - 1, object.y + chartHeight, object.colors.axis, "┗" .. string.rep("┯━", math.floor(chartWidth / 2))) + + local function fillVerticalPart(x1, y1, x2, y2) + local dx, dy = x2 - x1, y2 - y1 + local absdx, absdy = math.abs(dx), math.abs(dy) + if absdx >= absdy then + local step, y = dy / absdx, y1 + for x = x1, x2, (x1 < x2 and 1 or -1) do + local yFloor = math.floor(y) + screen.drawSemiPixelRectangle(math.floor(x), yFloor, 1, math.floor(object.y + chartHeight) * 2 - yFloor - 1, object.colors.chart) + y = y + step + end + else + local step, x = dx / absdy, x1 + for y = y1, y2, (y1 < y2 and 1 or -1) do + local yFloor = math.floor(y) + screen.drawSemiPixelRectangle(math.floor(x), yFloor, 1, math.floor(object.y + chartHeight) * 2 - yFloor - 1, object.colors.chart) + x = x + step + end + end + end + + -- chart + for i = 1, #valuesCopy - 1 do + local x = math.floor(chartX + (valuesCopy[i][1] - xMin) / dx * (chartWidth - 1)) + local y = math.floor(object.y + chartHeight - 1 - (valuesCopy[i][2] - yMin) / dy * (chartHeight - 1)) * 2 + local xNext = math.floor(chartX + (valuesCopy[i + 1][1] - xMin) / dx * (chartWidth - 1)) + local yNext = math.floor(object.y + chartHeight - 1 - (valuesCopy[i + 1][2] - yMin) / dy * (chartHeight - 1)) * 2 + if object.fillChartArea then + fillVerticalPart(x, y, xNext, yNext) + else + screen.drawSemiPixelLine(x, y, xNext, yNext, object.colors.chart) + end + end + + return object +end + +function GUI.chart(x, y, width, height, axisColor, axisValueColor, axisHelpersColor, chartColor, xAxisValueInterval, yAxisValueInterval, xAxisPostfix, yAxisPostfix, fillChartArea, values) + local object = GUI.object(x, y, width, height) + + object.colors = {axis = axisColor, chart = chartColor, axisValue = axisValueColor, helpers = axisHelpersColor} + object.draw = drawChart + object.values = values or {} + object.xAxisPostfix = xAxisPostfix + object.yAxisPostfix = yAxisPostfix + object.xAxisValueInterval = xAxisValueInterval + object.yAxisValueInterval = yAxisValueInterval + object.fillChartArea = fillChartArea + object.showYAxisValues = true + object.showXAxisValues = true + + return object +end + +-------------------------------------------------------------------------------- + +local function switchAndLabelDraw(switchAndLabel) + switchAndLabel.label.width = switchAndLabel.width + switchAndLabel.switch.localX = switchAndLabel.width - switchAndLabel.switch.width + + switchAndLabel.label.x, switchAndLabel.label.y = switchAndLabel.x + switchAndLabel.label.localX - 1, switchAndLabel.y + switchAndLabel.label.localY - 1 + switchAndLabel.switch.x, switchAndLabel.switch.y = switchAndLabel.x + switchAndLabel.switch.localX - 1, switchAndLabel.y + switchAndLabel.switch.localY - 1 + + switchAndLabel.label:draw() + switchAndLabel.switch:draw() + + return switchAndLabel +end + +function GUI.switchAndLabel(x, y, width, switchWidth, activeColor, passiveColor, pipeColor, textColor, text, switchState) + local switchAndLabel = GUI.container(x, y, width, 1) + + switchAndLabel.label = switchAndLabel:addChild(GUI.label(1, 1, width, 1, textColor, text)) + switchAndLabel.switch = switchAndLabel:addChild(GUI.switch(1, 1, switchWidth, activeColor, passiveColor, pipeColor, switchState)) + switchAndLabel.draw = switchAndLabelDraw + + return switchAndLabel +end + +-------------------------------------------------------------------------------- + +local function sliderDraw(object) + -- На всякий случай делаем значение не меньше минимального и не больше максимального + object.value = math.min(math.max(object.value, object.minimumValue), object.maximumValue) + + if object.showMaximumAndMinimumValues then + local stringMaximumValue, stringMinimumValue = tostring(object.roundValues and math.floor(object.maximumValue) or number.roundToDecimalPlaces(object.maximumValue, 2)), tostring(object.roundValues and math.floor(object.minimumValue) or number.roundToDecimalPlaces(object.minimumValue, 2)) + screen.drawText(object.x - unicode.len(stringMinimumValue) - 1, object.y, object.colors.value, stringMinimumValue) + screen.drawText(object.x + object.width + 1, object.y, object.colors.value, stringMaximumValue) + end + + if object.currentValuePrefix or object.currentValuePostfix then + local stringCurrentValue = (object.currentValuePrefix or "") .. (object.roundValues and math.floor(object.value) or number.roundToDecimalPlaces(object.value, 2)) .. (object.currentValuePostfix or "") + screen.drawText(math.floor(object.x + object.width / 2 - unicode.len(stringCurrentValue) / 2), object.y + 1, object.colors.value, stringCurrentValue) + end + + local activeWidth = number.round((object.value - object.minimumValue) / (object.maximumValue - object.minimumValue) * object.width) + screen.drawText(object.x, object.y, object.colors.passive, string.rep("━", object.width)) + screen.drawText(object.x, object.y, object.colors.active, string.rep("━", activeWidth)) + screen.drawText(activeWidth >= object.width and object.x + activeWidth - 1 or object.x + activeWidth, object.y, object.colors.pipe, "⬤") + + return object +end + +local function sliderEventHandler(workspace, object, e1, e2, e3, ...) + if e1 == "touch" or e1 == "drag" then + local clickPosition = e3 - object.x + + if clickPosition == 0 then + object.value = object.minimumValue + elseif clickPosition == object.width - 1 then + object.value = object.maximumValue + else + object.value = object.minimumValue + (clickPosition / object.width * (object.maximumValue - object.minimumValue)) + end + + workspace:draw() + + if object.onValueChanged then + object.onValueChanged(workspace, object, e1, e2, e3, ...) + end + end +end + +function GUI.slider(x, y, width, activeColor, passiveColor, pipeColor, valueColor, minimumValue, maximumValue, value, showMaximumAndMinimumValues, currentValuePrefix, currentValuePostfix) + local object = GUI.object(x, y, width, 1) + + object.eventHandler = sliderEventHandler + object.colors = {active = activeColor, passive = passiveColor, pipe = pipeColor, value = valueColor} + object.draw = sliderDraw + object.minimumValue = minimumValue + object.maximumValue = maximumValue + object.value = value + object.showMaximumAndMinimumValues = showMaximumAndMinimumValues + object.currentValuePrefix = currentValuePrefix + object.currentValuePostfix = currentValuePostfix + object.roundValues = false + + return object +end + +-------------------------------------------------------------------------------- + +local function switchDraw(switch) + screen.drawText(switch.x - 1, switch.y, switch.colors.passive, "⠰") + screen.drawRectangle(switch.x, switch.y, switch.width, 1, switch.colors.passive, 0x0, " ") + screen.drawText(switch.x + switch.width, switch.y, switch.colors.passive, "⠆") + + screen.drawText(switch.x - 1, switch.y, switch.colors.active, "⠰") + screen.drawRectangle(switch.x, switch.y, switch.pipePosition - 1, 1, switch.colors.active, 0x0, " ") + + screen.drawText(switch.x + switch.pipePosition - 2, switch.y, switch.colors.pipe, "⠰") + screen.drawRectangle(switch.x + switch.pipePosition - 1, switch.y, 2, 1, switch.colors.pipe, 0x0, " ") + screen.drawText(switch.x + switch.pipePosition + 1, switch.y, switch.colors.pipe, "⠆") + + return switch +end + +local function switchSetState(switch, state) + switch.state = state + switch.pipePosition = switch.state and switch.width - 1 or 1 + + return switch +end + +local function switchEventHandler(workspace, switch, e1, ...) + if e1 == "touch" then + local eventData = {...} + + switch.state = not switch.state + switch:addAnimation( + function(animation) + if switch.state then + switch.pipePosition = number.round(1 + animation.position * (switch.width - 2)) + else + switch.pipePosition = number.round(1 + (1 - animation.position) * (switch.width - 2)) + end + end, + function(animation) + animation:remove() + if switch.onStateChanged then + switch.onStateChanged(switch, e1, table.unpack(eventData)) + end + end + ):start(switch.animationDuration) + end +end + +function GUI.switch(x, y, width, activeColor, passiveColor, pipeColor, state) + local switch = GUI.object(x, y, width, 1) + + switch.pipePosition = 1 + switch.eventHandler = switchEventHandler + switch.colors = { + active = activeColor, + passive = passiveColor, + pipe = pipeColor, + } + switch.draw = switchDraw + switch.state = state or false + switch.update = switchUpdate + switch.animated = true + switch.animationDuration = GUI.SWITCH_ANIMATION_DURATION + switch.setState = switchSetState + + switch:setState(state) + + return switch +end + +-------------------------------------------------------------------------------- + +local function layoutCheckCell(layout, column, row) + if column < 1 or column > #layout.columnSizes or row < 1 or row > #layout.rowSizes then + error("Specified grid position (" .. tostring(column) .. "x" .. tostring(row) .. ") is out of layout grid range") + end +end + +local function layoutGetAbsoluteTotalSize(array) + local absoluteTotalSize = 0 + for i = 1, #array do + if array[i].sizePolicy == GUI.SIZE_POLICY_ABSOLUTE then + absoluteTotalSize = absoluteTotalSize + array[i].size + end + end + return absoluteTotalSize +end + +local function layoutGetCalculatedSize(array, index, dependency) + if array[index].sizePolicy == GUI.SIZE_POLICY_RELATIVE then + array[index].calculatedSize = array[index].size * dependency + else + array[index].calculatedSize = array[index].size + end +end + +local function layoutUpdate(layout) + local columnPercentageTotalSize, rowPercentageTotalSize = layout.width - layoutGetAbsoluteTotalSize(layout.columnSizes), layout.height - layoutGetAbsoluteTotalSize(layout.rowSizes) + for row = 1, #layout.rowSizes do + layoutGetCalculatedSize(layout.rowSizes, row, rowPercentageTotalSize) + for column = 1, #layout.columnSizes do + layoutGetCalculatedSize(layout.columnSizes, column, columnPercentageTotalSize) + layout.cells[row][column].childrenWidth, layout.cells[row][column].childrenHeight = 0, 0 + end + end + + -- Подготавливаем объекты к расположению и подсчитываем тотальные размеры + local child, layoutRow, layoutColumn, cell + for i = 1, #layout.children do + child = layout.children[i] + + if not child.hidden then + layoutRow, layoutColumn = child.layoutRow, child.layoutColumn + + -- Проверка на позицию в сетке + if layoutRow >= 1 and layoutRow <= #layout.rowSizes and layoutColumn >= 1 and layoutColumn <= #layout.columnSizes then + cell = layout.cells[layoutRow][layoutColumn] + -- Авто-фиттинг объектов + if cell.horizontalFitting then + child.width = number.round(layout.columnSizes[layoutColumn].calculatedSize - cell.horizontalFittingRemove) + end + + if cell.verticalFitting then + child.height = number.round(layout.rowSizes[layoutRow].calculatedSize - cell.verticalFittingRemove) + end + + -- Направление и расчет размеров + if cell.direction == GUI.DIRECTION_HORIZONTAL then + cell.childrenWidth = cell.childrenWidth + child.width + cell.spacing + cell.childrenHeight = math.max(cell.childrenHeight, child.height) + else + cell.childrenWidth = math.max(cell.childrenWidth, child.width) + cell.childrenHeight = cell.childrenHeight + child.height + cell.spacing + end + else + error("Layout child with index " .. i .. " has been assigned to cell (" .. layoutColumn .. "x" .. layoutRow .. ") out of layout grid range") + end + end + end + + -- Высчитываем стартовую позицию объектов ячейки + local x, y = 1, 1 + for row = 1, #layout.rowSizes do + for column = 1, #layout.columnSizes do + cell = layout.cells[row][column] + cell.x, cell.y = GUI.getAlignmentCoordinates( + x, + y, + layout.columnSizes[column].calculatedSize, + layout.rowSizes[row].calculatedSize, + cell.horizontalAlignment, + cell.verticalAlignment, + cell.childrenWidth - (cell.direction == GUI.DIRECTION_HORIZONTAL and cell.spacing or 0), + cell.childrenHeight - (cell.direction == GUI.DIRECTION_VERTICAL and cell.spacing or 0) + ) + + -- Учитываем отступы от краев ячейки + if cell.horizontalMargin ~= 0 or cell.verticalMargin ~= 0 then + cell.x, cell.y = GUI.getMarginCoordinates( + cell.x, + cell.y, + cell.horizontalAlignment, + cell.verticalAlignment, + cell.horizontalMargin, + cell.verticalMargin + ) + end + + x = x + layout.columnSizes[column].calculatedSize + end + + x, y = 1, y + layout.rowSizes[row].calculatedSize + end + + -- Размещаем все объекты + for i = 1, #layout.children do + child = layout.children[i] + + if not child.hidden then + cell = layout.cells[child.layoutRow][child.layoutColumn] + + child.localX, cell.localY = GUI.getAlignmentCoordinates( + cell.x, + cell.y, + cell.childrenWidth, + cell.childrenHeight, + cell.horizontalAlignment, + cell.verticalAlignment, + child.width, + child.height + ) + + if cell.direction == GUI.DIRECTION_HORIZONTAL then + child.localX, child.localY = math.floor(cell.x), math.floor(cell.localY) + cell.x = cell.x + child.width + cell.spacing + else + child.localX, child.localY = math.floor(child.localX), math.floor(cell.y) + cell.y = cell.y + child.height + cell.spacing + end + end + end +end + +local function layoutSetPosition(layout, column, row, object) + layoutCheckCell(layout, column, row) + object.layoutRow = row + object.layoutColumn = column + + return object +end + +local function layoutSetDirection(layout, column, row, direction) + layoutCheckCell(layout, column, row) + layout.cells[row][column].direction = direction + + return layout +end + +local function layoutSetSpacing(layout, column, row, spacing) + layoutCheckCell(layout, column, row) + layout.cells[row][column].spacing = spacing + + return layout +end + +local function layoutSetAlignment(layout, column, row, horizontalAlignment, verticalAlignment) + layoutCheckCell(layout, column, row) + layout.cells[row][column].horizontalAlignment, layout.cells[row][column].verticalAlignment = horizontalAlignment, verticalAlignment + + return layout +end + +local function layoutGetMargin(layout, column, row) + layoutCheckCell(layout, column, row) + + return layout.cells[row][column].horizontalMargin, layout.cells[row][column].verticalMargin +end + +local function layoutSetMargin(layout, column, row, horizontalMargin, verticalMargin) + layoutCheckCell(layout, column, row) + layout.cells[row][column].horizontalMargin = horizontalMargin + layout.cells[row][column].verticalMargin = verticalMargin + + return layout +end + +local function layoutNewCell() + return { + horizontalAlignment = GUI.ALIGNMENT_HORIZONTAL_CENTER, + verticalAlignment = GUI.ALIGNMENT_VERTICAL_CENTER, + horizontalMargin = 0, + verticalMargin = 0, + direction = GUI.DIRECTION_VERTICAL, + spacing = 1 + } +end + +local function layoutCalculatePercentageSize(changingExistent, array, index) + if array[index].sizePolicy == GUI.SIZE_POLICY_RELATIVE then + local allPercents, beforeFromIndexPercents = 0, 0 + for i = 1, #array do + if array[i].sizePolicy == GUI.SIZE_POLICY_RELATIVE then + allPercents = allPercents + array[i].size + + if i <= index then + beforeFromIndexPercents = beforeFromIndexPercents + array[i].size + end + end + end + + local modifyer + if changingExistent then + if beforeFromIndexPercents > 1 then + error("Layout summary percentage > 100% at index " .. index) + end + modifyer = (1 - beforeFromIndexPercents) / (allPercents - beforeFromIndexPercents) + else + modifyer = (1 - array[index].size) / (allPercents - array[index].size) + end + + for i = changingExistent and index + 1 or 1, #array do + if array[i].sizePolicy == GUI.SIZE_POLICY_RELATIVE and i ~= index then + array[i].size = modifyer * array[i].size + end + end + end +end + +local function layoutSetColumnWidth(layout, column, sizePolicy, size) + layout.columnSizes[column].sizePolicy, layout.columnSizes[column].size = sizePolicy, size + layoutCalculatePercentageSize(true, layout.columnSizes, column) + + return layout +end + +local function layoutSetRowHeight(layout, row, sizePolicy, size) + layout.rowSizes[row].sizePolicy, layout.rowSizes[row].size = sizePolicy, size + layoutCalculatePercentageSize(true, layout.rowSizes, row) + + return layout +end + +local function layoutAddColumn(layout, sizePolicy, size) + for i = 1, #layout.rowSizes do + table.insert(layout.cells[i], layoutNewCell()) + end + + table.insert(layout.columnSizes, { + sizePolicy = sizePolicy, + size = size + }) + layoutCalculatePercentageSize(false, layout.columnSizes, #layout.columnSizes) + + return layout +end + +local function layoutAddRow(layout, sizePolicy, size) + local row = {} + for i = 1, #layout.columnSizes do + table.insert(row, layoutNewCell()) + end + + table.insert(layout.cells, row) + table.insert(layout.rowSizes, { + sizePolicy = sizePolicy, + size = size + }) + + layoutCalculatePercentageSize(false, layout.rowSizes, #layout.rowSizes) + + return layout +end + +local function layoutRemoveRow(layout, row) + table.remove(layout.cells, row) + + layout.rowSizes[row].size = 0 + layoutCalculatePercentageSize(false, layout.rowSizes, row) + + table.remove(layout.rowSizes, row) + + return layout +end + +local function layoutRemoveColumn(layout, column) + for i = 1, #layout.rowSizes do + table.remove(layout.cells[i], column) + end + + layout.columnSizes[column].size = 0 + layoutCalculatePercentageSize(false, layout.columnSizes, column) + + table.remove(layout.columnSizes, column) + + return layout +end + +local function layoutSetGridSize(layout, columnCount, rowCount) + layout.cells = {} + layout.rowSizes = {} + layout.columnSizes = {} + + local rowSize, columnSize = 1 / rowCount, 1 / columnCount + for i = 1, rowCount do + layoutAddRow(layout, GUI.SIZE_POLICY_RELATIVE, 1 / i) + end + + for i = 1, columnCount do + layoutAddColumn(layout, GUI.SIZE_POLICY_RELATIVE, 1 / i) + end + + return layout +end + +local function layoutDraw(layout) + layout:update() + containerDraw(layout) + + if layout.showGrid then + local x, y = layout.x, layout.y + for j = 1, #layout.columnSizes do + for i = 1, #layout.rowSizes do + screen.drawFrame( + number.round(x), + number.round(y), + number.round(layout.columnSizes[j].calculatedSize), + number.round(layout.rowSizes[i].calculatedSize), + 0xFF0000 + ) + y = y + layout.rowSizes[i].calculatedSize + end + x, y = x + layout.columnSizes[j].calculatedSize, layout.y + end + end +end + +local function layoutFitToChildrenSize(layout, column, row) + layout.width, layout.height = 0, 0 + + for i = 1, #layout.children do + if not layout.children[i].hidden then + if layout.cells[row][column].direction == GUI.DIRECTION_HORIZONTAL then + layout.width = layout.width + layout.children[i].width + layout.cells[row][column].spacing + layout.height = math.max(layout.height, layout.children[i].height) + else + layout.width = math.max(layout.width, layout.children[i].width) + layout.height = layout.height + layout.children[i].height + layout.cells[row][column].spacing + end + end + end + + if layout.cells[row][column].direction == GUI.DIRECTION_HORIZONTAL then + layout.width = layout.width - layout.cells[row][column].spacing + else + layout.height = layout.height - layout.cells[row][column].spacing + end + + return layout +end + +local function layoutSetFitting(layout, column, row, horizontal, vertical, horizontalRemove, verticalRemove) + layoutCheckCell(layout, column, row) + layout.cells[row][column].horizontalFitting = horizontal + layout.cells[row][column].verticalFitting = vertical + layout.cells[row][column].horizontalFittingRemove = horizontalRemove or 0 + layout.cells[row][column].verticalFittingRemove = verticalRemove or 0 + + return layout +end + +local function layoutAddChild(layout, object, ...) + object.layoutRow = layout.defaultRow + object.layoutColumn = layout.defaultColumn + containerAddChild(layout, object, ...) + + return object +end + +function GUI.layout(x, y, width, height, columnCount, rowCount) + local layout = GUI.container(x, y, width, height) + + layout.defaultRow = 1 + layout.defaultColumn = 1 + + layout.addRow = layoutAddRow + layout.addColumn = layoutAddColumn + layout.removeRow = layoutRemoveRow + layout.removeColumn = layoutRemoveColumn + + layout.setRowHeight = layoutSetRowHeight + layout.setColumnWidth = layoutSetColumnWidth + + layout.setPosition = layoutSetPosition + layout.setDirection = layoutSetDirection + layout.setGridSize = layoutSetGridSize + layout.setSpacing = layoutSetSpacing + layout.setAlignment = layoutSetAlignment + layout.setMargin = layoutSetMargin + layout.getMargin = layoutGetMargin + + layout.fitToChildrenSize = layoutFitToChildrenSize + layout.setFitting = layoutSetFitting + + layout.update = layoutUpdate + layout.addChild = layoutAddChild + layout.draw = layoutDraw + + layout:setGridSize(columnCount, rowCount) + + return layout +end + +-------------------------------------------------------------------------------- + +local function filesystemDialogDraw(filesystemDialog) + if filesystemDialog.extensionComboBox.hidden then + filesystemDialog.input.width = filesystemDialog.cancelButton.localX - 4 + else + filesystemDialog.input.width = filesystemDialog.extensionComboBox.localX - 3 + end + + if filesystemDialog.IOMode == GUI.IO_MODE_SAVE then + filesystemDialog.submitButton.disabled = not filesystemDialog.input.text or filesystemDialog.input.text == "" + else + filesystemDialog.input.text = filesystemDialog.filesystemTree.selectedItem or "" + filesystemDialog.submitButton.disabled = not filesystemDialog.filesystemTree.selectedItem + end + + containerDraw(filesystemDialog) + GUI.drawShadow(filesystemDialog.x, filesystemDialog.y, filesystemDialog.width, filesystemDialog.height, GUI.CONTEXT_MENU_SHADOW_TRANSPARENCY, true) + + return filesystemDialog +end + +local function filesystemDialogSetMode(filesystemDialog, IOMode, filesystemMode) + filesystemDialog.IOMode = IOMode + filesystemDialog.filesystemMode = filesystemMode + + if filesystemDialog.IOMode == GUI.IO_MODE_SAVE then + filesystemDialog.filesystemTree.showMode = GUI.IO_MODE_DIRECTORY + filesystemDialog.filesystemTree.selectionMode = GUI.IO_MODE_DIRECTORY + filesystemDialog.input.disabled = false + filesystemDialog.extensionComboBox.hidden = filesystemDialog.filesystemMode ~= GUI.IO_MODE_FILE or not filesystemDialog.filesystemTree.extensionFilters + else + if filesystemDialog.filesystemMode == GUI.IO_MODE_FILE then + filesystemDialog.filesystemTree.showMode = GUI.IO_MODE_BOTH + filesystemDialog.filesystemTree.selectionMode = GUI.IO_MODE_FILE + else + filesystemDialog.filesystemTree.showMode = GUI.IO_MODE_DIRECTORY + filesystemDialog.filesystemTree.selectionMode = GUI.IO_MODE_DIRECTORY + end + + filesystemDialog.input.disabled = true + filesystemDialog.extensionComboBox.hidden = true + end +end + +local function filesystemDialogAddExtensionFilter(filesystemDialog, extension) + filesystemDialog.extensionComboBox:addItem(extension) + filesystemDialog.extensionComboBox.width = math.max(filesystemDialog.extensionComboBox.width, unicode.len(extension) + 3) + filesystemDialog.extensionComboBox.localX = filesystemDialog.cancelButton.localX - filesystemDialog.extensionComboBox.width - 2 + filesystemDialog.filesystemTree:addExtensionFilter(extension) + + filesystemDialog:setMode(filesystemDialog.IOMode, filesystemDialog.filesystemMode) +end + +local function filesystemDialogExpandPath(filesystemDialog, ...) + filesystemDialog.filesystemTree:expandPath(...) +end + +function GUI.filesystemDialog(x, y, width, height, submitButtonText, cancelButtonText, placeholderText, path) + local filesystemDialog = GUI.container(x, y, width, height) + + filesystemDialog:addChild(GUI.panel(1, height - 2, width, 3, 0xD2D2D2)) + + filesystemDialog.cancelButton = filesystemDialog:addChild(GUI.adaptiveRoundedButton(1, height - 1, 1, 0, 0xE1E1E1, 0x3C3C3C, 0x3C3C3C, 0xE1E1E1, cancelButtonText)) + filesystemDialog.submitButton = filesystemDialog:addChild(GUI.adaptiveRoundedButton(1, height - 1, 1, 0, 0x3C3C3C, 0xE1E1E1, 0xE1E1E1, 0x3C3C3C, submitButtonText)) + filesystemDialog.submitButton.localX = filesystemDialog.width - filesystemDialog.submitButton.width - 1 + filesystemDialog.cancelButton.localX = filesystemDialog.submitButton.localX - filesystemDialog.cancelButton.width - 2 + + filesystemDialog.extensionComboBox = filesystemDialog:addChild(GUI.comboBox(1, height - 1, 1, 1, 0xE1E1E1, 0x696969, 0xC3C3C3, 0x878787)) + filesystemDialog.extensionComboBox.hidden = true + + filesystemDialog.input = filesystemDialog:addChild(GUI.input(2, height - 1, 1, 1, 0xE1E1E1, 0x696969, 0x969696, 0xE1E1E1, 0x3C3C3C, "", placeholderText)) + + filesystemDialog.filesystemTree = filesystemDialog:addChild(GUI.filesystemTree(1, 1, width, height - 3, 0xE1E1E1, 0x3C3C3C, 0x3C3C3C, 0xA5A5A5, 0x3C3C3C, 0xE1E1E1, 0xB4B4B4, 0xA5A5A5, 0xC3C3C3, 0x4B4B4B)) + filesystemDialog.filesystemTree.workPath = path + filesystemDialog.animationDuration = GUI.FILESYSTEM_DIALOG_ANIMATION_DURATION + + filesystemDialog.draw = filesystemDialogDraw + filesystemDialog.setMode = filesystemDialogSetMode + filesystemDialog.addExtensionFilter = filesystemDialogAddExtensionFilter + + filesystemDialog.expandPath = filesystemDialogExpandPath + filesystemDialog:setMode(GUI.IO_MODE_OPEN, GUI.IO_MODE_FILE) + + return filesystemDialog +end + +local function filesystemDialogShow(filesystemDialog) + filesystemDialog.filesystemTree:updateFileList() + filesystemDialog:addAnimation( + function(animation) + filesystemDialog.localY = math.floor(1 + (1.0 - animation.position) * (-filesystemDialog.height)) + end, + function(animation) + animation:remove() + end + ):start(filesystemDialog.animationDuration) + + return filesystemDialog +end + +-------------------------------------------------------------------------------- + +function GUI.addFilesystemDialog(parentContainer, addPanel, ...) + local container = GUI.addBackgroundContainer(parentContainer, addPanel, false, nil) + + local filesystemDialog = container:addChild(GUI.filesystemDialog(1, 1, ...)) + filesystemDialog.localX = math.floor(container.width / 2 - filesystemDialog.width / 2) + filesystemDialog.localY = -filesystemDialog.height + + local function onAnyTouch() + container:remove() + filesystemDialog.firstParent:draw() + end + + filesystemDialog.cancelButton.onTouch = function() + onAnyTouch() + + if filesystemDialog.onCancel then + filesystemDialog.onCancel() + end + end + + filesystemDialog.submitButton.onTouch = function() + onAnyTouch() + + local path = filesystemDialog.filesystemTree.selectedItem or filesystemDialog.filesystemTree.workPath or "/" + if filesystemDialog.IOMode == GUI.IO_MODE_SAVE then + path = path .. filesystemDialog.input.text + + if filesystemDialog.filesystemMode == GUI.IO_MODE_FILE then + local selectedItem = filesystemDialog.extensionComboBox:getItem(filesystemDialog.extensionComboBox.selectedItem) + path = path .. (selectedItem and selectedItem.text or "") + else + path = path .. "/" + end + end + + if filesystemDialog.onSubmit then + filesystemDialog.onSubmit(path) + end + end + + filesystemDialog.show = filesystemDialogShow + + return filesystemDialog +end + +-------------------------------------------------------------------------------- + +local function filesystemChooserDraw(object) + local tipWidth = object.height * 2 - 1 + local y = math.floor(object.y + object.height / 2) + + screen.drawRectangle(object.x, object.y, object.width - tipWidth, object.height, object.colors.background, object.colors.text, " ") + screen.drawRectangle(object.x + object.width - tipWidth, object.y, tipWidth, object.height, object.pressed and object.colors.tipText or object.colors.tipBackground, object.pressed and object.colors.tipBackground or object.colors.tipText, " ") + screen.drawText(object.x + object.width - math.floor(tipWidth / 2) - 1, y, object.pressed and object.colors.tipBackground or object.colors.tipText, "…") + screen.drawText(object.x + 1, y, object.colors.text, text.limit(object.path or object.placeholderText, object.width - tipWidth - 2, "left")) + + return filesystemChooser +end + +local function filesystemChooserAddExtensionFilter(object, extension) + object.extensionFilters[unicode.lower(extension)] = true +end + +local function filesystemChooserSetMode(object, IOMode, filesystemMode) + object.IOMode = IOMode + object.filesystemMode = filesystemMode +end + +local function filesystemChooserEventHandler(workspace, object, e1) + if e1 == "touch" then + object.pressed = true + workspace:draw() + + local filesystemDialog = GUI.addFilesystemDialog(workspace, false, 50, math.floor(workspace.height * 0.8), object.submitButtonText, object.cancelButtonText, object.placeholderText, object.filesystemDialogPath) + + for key in pairs(object.extensionFilters) do + filesystemDialog:addExtensionFilter(key) + end + + filesystemDialog:setMode(object.IOMode, object.filesystemMode) + + if object.path and #object.path > 0 then + -- local path = object.path:gsub("/+", "/") + filesystemDialog.filesystemTree.selectedItem = object.IOMode == GUI.IO_MODE_OPEN and object.path or filesystem.path(object.path) + filesystemDialog.input.text = filesystem.name(object.path) + filesystemDialog:expandPath(object.IOMode == GUI.IO_MODE_OPEN and filesystem.path(object.path) or filesystem.path(filesystem.path(object.path))) + end + + filesystemDialog.onCancel = function() + object.pressed = false + workspace:draw() + end + + filesystemDialog.onSubmit = function(path) + object.path = path + filesystemDialog.onCancel() + if object.onSubmit then + object.onSubmit(object.path) + end + end + + filesystemDialog:show() + end +end + +function GUI.filesystemChooser(x, y, width, height, backgroundColor, textColor, tipBackgroundColor, tipTextColor, path, submitButtonText, cancelButtonText, placeholderText, filesystemDialogPath) + local object = GUI.object(x, y, width, height) + + object.eventHandler = comboBoxEventHandler + object.colors = { + tipBackground = tipBackgroundColor, + tipText = tipTextColor, + text = textColor, + background = backgroundColor + } + + object.submitButtonText = submitButtonText + object.cancelButtonText = cancelButtonText + object.placeholderText = placeholderText + object.pressed = false + object.path = path + object.filesystemDialogPath = filesystemDialogPath + object.filesystemMode = GUI.IO_MODE_FILE + object.IOMode = GUI.IO_MODE_OPEN + object.extensionFilters = {} + + object.draw = filesystemChooserDraw + object.eventHandler = filesystemChooserEventHandler + object.addExtensionFilter = filesystemChooserAddExtensionFilter + object.setMode = filesystemChooserSetMode + + return object +end + +-------------------------------------------------------------------------------- + +local function resizerDraw(object) + local horizontalMode, x, y, symbol = object.width >= object.height + + if horizontalMode then + screen.drawText(object.x, math.floor(object.y + object.height / 2), object.colors.helper, string.rep("━", object.width)) + + if object.lastTouchX then + screen.drawText(object.lastTouchX, object.lastTouchY, object.colors.arrow, "↑") + end + else + local x = math.floor(object.x + object.width / 2) + local bufferWidth, bufferHeight, index = screen.getResolution() + + for i = object.y, object.y + object.height - 1 do + if x >= 1 and x <= bufferWidth and i >= 1 and i <= bufferHeight then + index = screen.getIndex(x, i) + screen.rawSet(index, screen.rawGet(index), object.colors.helper, "┃") + end + end + + if object.lastTouchX then + screen.drawText(object.lastTouchX - 1, object.lastTouchY, object.colors.arrow, "←→") + end + end +end + +local function resizerEventHandler(workspace, object, e1, e2, e3, e4) + if e1 == "touch" then + object.lastTouchX, object.lastTouchY = e3, e4 + workspace:draw() + elseif e1 == "drag" and object.lastTouchX then + if object.onResize then + object.onResize(e3 - object.lastTouchX, e4 - object.lastTouchY) + end + + object.lastTouchX, object.lastTouchY = e3, e4 + workspace:draw() + elseif e1 == "drop" then + if object.onResizeFinished then + object.onResizeFinished() + end + + object.lastTouchX, object.lastTouchY = nil, nil + workspace:draw() + end +end + +function GUI.resizer(x, y, width, height, helperColor, arrowColor) + local object = GUI.object(x, y, width, height) + + object.colors = { + helper = helperColor, + arrow = arrowColor + } + + object.draw = resizerDraw + object.eventHandler = resizerEventHandler + + return object +end + +-------------------------------------------------------------------------------- + +local function scrollBarDraw(scrollBar) + local isVertical = scrollBar.height > scrollBar.width + local valuesDelta = scrollBar.maximumValue - scrollBar.minimumValue + local part = scrollBar.value / valuesDelta + + if isVertical then + local barSize = math.ceil(scrollBar.shownValueCount / valuesDelta * scrollBar.height) + local halfBarSize = math.floor(barSize / 2) + + scrollBar.ghostPosition.y = scrollBar.y + halfBarSize + scrollBar.ghostPosition.height = scrollBar.height - barSize + + if scrollBar.thin then + local y1 = math.floor(scrollBar.ghostPosition.y + part * scrollBar.ghostPosition.height - halfBarSize) + local y2 = y1 + barSize - 1 + local background + + for y = scrollBar.y, scrollBar.y + scrollBar.height - 1 do + background = screen.get(scrollBar.x, y) + screen.set(scrollBar.x, y, background, y >= y1 and y <= y2 and scrollBar.colors.foreground or scrollBar.colors.background, "┃") + end + else + screen.drawRectangle(scrollBar.x, scrollBar.y, scrollBar.width, scrollBar.height, scrollBar.colors.background, scrollBar.colors.foreground, " ") + screen.drawRectangle( + scrollBar.x, + math.floor(scrollBar.ghostPosition.y + part * scrollBar.ghostPosition.height - halfBarSize), + scrollBar.width, + barSize, + scrollBar.colors.foreground, 0x0, " " + ) + end + else + local barSize = math.ceil(scrollBar.shownValueCount / valuesDelta * scrollBar.width) + local halfBarSize = math.floor(barSize / 2) + + scrollBar.ghostPosition.x = scrollBar.x + halfBarSize + scrollBar.ghostPosition.width = scrollBar.width - barSize + + if scrollBar.thin then + local x1 = math.floor(scrollBar.ghostPosition.x + part * scrollBar.ghostPosition.width - halfBarSize) + local x2 = x1 + barSize - 1 + local background + + for x = scrollBar.x, scrollBar.x + scrollBar.width - 1 do + background = screen.get(x, scrollBar.y) + screen.set(x, scrollBar.y, background, x >= x1 and x <= x2 and scrollBar.colors.foreground or scrollBar.colors.background, "⠤") + end + else + screen.drawRectangle(scrollBar.x, scrollBar.y, scrollBar.width, scrollBar.height, scrollBar.colors.background, scrollBar.colors.foreground, " ") + screen.drawRectangle( + math.floor(scrollBar.ghostPosition.x + part * scrollBar.ghostPosition.width - halfBarSize), + scrollBar.y, + barSize, + scrollBar.height, + scrollBar.colors.foreground, 0x0, " " + ) + end + end + + return scrollBar +end + +local function scrollBarEventHandler(workspace, object, e1, e2, e3, e4, e5, ...) + local newValue = object.value + + if e1 == "touch" or e1 == "drag" then + if object.height > object.width then + if e4 == object.y + object.height - 1 then + newValue = object.maximumValue + else + newValue = object.minimumValue + (e4 - object.y) / object.height * (object.maximumValue - object.minimumValue) + end + else + if e3 == object.x + object.width - 1 then + newValue = object.maximumValue + else + newValue = object.minimumValue + (e3 - object.x) / object.width * (object.maximumValue - object.minimumValue) + end + end + elseif e1 == "scroll" then + if e5 == 1 then + if object.value >= object.minimumValue + object.onScrollValueIncrement then + newValue = object.value - object.onScrollValueIncrement + else + newValue = object.minimumValue + end + else + if object.value <= object.maximumValue - object.onScrollValueIncrement then + newValue = object.value + object.onScrollValueIncrement + else + newValue = object.maximumValue + end + end + end + + if e1 == "touch" or e1 == "drag" or e1 == "scroll" then + object.value = newValue + if object.onTouch then + object.onTouch(workspace, object, e1, e2, e3, e4, e5, ...) + end + + workspace:draw() + end +end + +function GUI.scrollBar(x, y, width, height, backgroundColor, foregroundColor, minimumValue, maximumValue, value, shownValueCount, onScrollValueIncrement, thin) + local scrollBar = GUI.object(x, y, width, height) + + scrollBar.eventHandler = scrollBarEventHandler + scrollBar.maximumValue = maximumValue + scrollBar.minimumValue = minimumValue + scrollBar.value = value + scrollBar.onScrollValueIncrement = onScrollValueIncrement + scrollBar.shownValueCount = shownValueCount + scrollBar.thin = thin + scrollBar.colors = { + background = backgroundColor, + foreground = foregroundColor, + } + scrollBar.ghostPosition = {} + scrollBar.draw = scrollBarDraw + + return scrollBar +end + +-------------------------------------------------------------------------------- + +local function treeDraw(tree) + local y, yEnd, showScrollBar = tree.y, tree.y + tree.height - 1, #tree.items > tree.height + local textLimit = tree.width - (showScrollBar and 1 or 0) + + if tree.colors.default.background then + screen.drawRectangle(tree.x, tree.y, tree.width, tree.height, tree.colors.default.background, tree.colors.default.expandable, " ") + end + + for i = tree.fromItem, #tree.items do + local textColor, arrowColor, text = tree.colors.default.notExpandable, tree.colors.default.arrow, tree.items[i].expandable and "■ " or "□ " + + if tree.selectedItem == tree.items[i].definition then + textColor, arrowColor = tree.colors.selected.any, tree.colors.selected.arrow + screen.drawRectangle(tree.x, y, tree.width, 1, tree.colors.selected.background, textColor, " ") + else + if tree.items[i].expandable then + textColor = tree.colors.default.expandable + elseif tree.items[i].disabled then + textColor = tree.colors.disabled + end + end + + if tree.items[i].expandable then + screen.drawText(tree.x + tree.items[i].offset, y, arrowColor, tree.expandedItems[tree.items[i].definition] and "▽" or "▷") + end + + screen.drawText(tree.x + tree.items[i].offset + 2, y, textColor, unicode.sub(text .. tree.items[i].name, 1, textLimit - tree.items[i].offset - 2)) + + y = y + 1 + if y > yEnd then break end + end + + if showScrollBar then + local scrollBar = tree.scrollBar + scrollBar.x = tree.x + tree.width - 1 + scrollBar.y = tree.y + scrollBar.width = 1 + scrollBar.height = tree.height + scrollBar.colors.background = tree.colors.scrollBar.background + scrollBar.colors.foreground = tree.colors.scrollBar.foreground + scrollBar.minimumValue = 1 + scrollBar.maximumValue = #tree.items + scrollBar.value = tree.fromItem + scrollBar.shownValueCount = tree.height + scrollBar.onScrollValueIncrement = 1 + scrollBar.thin = true + + scrollBar:draw() + end + + return tree +end + +local function treeEventHandler(workspace, tree, e1, e2, e3, e4, e5, ...) + if e1 == "touch" then + local i = e4 - tree.y + tree.fromItem + if tree.items[i] then + if + tree.items[i].expandable and + ( + tree.selectionMode == GUI.IO_MODE_FILE or + e3 >= tree.x + tree.items[i].offset - 1 and e3 <= tree.x + tree.items[i].offset + 1 + ) + then + if tree.expandedItems[tree.items[i].definition] then + tree.expandedItems[tree.items[i].definition] = nil + else + tree.expandedItems[tree.items[i].definition] = true + end + + if tree.onItemExpanded then + tree.onItemExpanded(tree.selectedItem, e1, e2, e3, e4, e5, ...) + end + else + if + ( + tree.selectionMode == GUI.IO_MODE_BOTH or + tree.selectionMode == GUI.IO_MODE_DIRECTORY and tree.items[i].expandable or + tree.selectionMode == GUI.IO_MODE_FILE + ) and not tree.items[i].disabled + then + tree.selectedItem = tree.items[i].definition + + if tree.onItemSelected then + tree.onItemSelected(tree.selectedItem, e1, e2, e3, e4, e5, ...) + end + end + end + + workspace:draw() + end + elseif e1 == "scroll" then + if e5 == 1 then + if tree.fromItem > 1 then + tree.fromItem = tree.fromItem - 1 + workspace:draw() + end + else + if tree.fromItem < #tree.items then + tree.fromItem = tree.fromItem + 1 + workspace:draw() + end + end + end +end + +local function treeAddItem(tree, name, definition, offset, expandable, disabled) + local item = { + name = name, + expandable = expandable, + offset = offset or 0, + definition = definition, + disabled = disabled + } + table.insert(tree.items, item) + + return item +end + +function GUI.tree(x, y, width, height, backgroundColor, expandableColor, notExpandableColor, arrowColor, backgroundSelectedColor, anySelectionColor, arrowSelectionColor, disabledColor, scrollBarBackground, scrollBarForeground, showMode, selectionMode) + local tree = GUI.object(x, y, width, height) + + tree.eventHandler = treeEventHandler + tree.colors = { + default = { + background = backgroundColor, + expandable = expandableColor, + notExpandable = notExpandableColor, + arrow = arrowColor, + }, + selected = { + background = backgroundSelectedColor, + any = anySelectionColor, + arrow = arrowSelectionColor, + }, + scrollBar = { + background = scrollBarBackground, + foreground = scrollBarForeground + }, + disabled = disabledColor + } + tree.items = {} + tree.fromItem = 1 + tree.selectedItem = nil + tree.expandedItems = {} + + tree.scrollBar = GUI.scrollBar(1, 1, 1, 1, 0x0, 0x0, 1, 1, 1, 1, 1) + + tree.showMode = showMode + tree.selectionMode = selectionMode + tree.eventHandler = treeEventHandler + tree.addItem = treeAddItem + tree.draw = treeDraw + + return tree +end + +-------------------------------------------------------------------------------- + +local function filesystemTreeUpdateFileListRecursively(tree, path, offset) + local list = filesystem.list(path) + + local i, expandables = 1, {} + while i <= #list do + if filesystem.isDirectory(path .. list[i]) then + table.insert(expandables, list[i]) + table.remove(list, i) + else + i = i + 1 + end + end + + table.sort(expandables, function(a, b) return unicode.lower(a) < unicode.lower(b) end) + table.sort(list, function(a, b) return unicode.lower(a) < unicode.lower(b) end) + + if tree.showMode == GUI.IO_MODE_BOTH or tree.showMode == GUI.IO_MODE_DIRECTORY then + for i = 1, #expandables do + tree:addItem(filesystem.name(expandables[i]), path .. expandables[i], offset, true) + + if tree.expandedItems[path .. expandables[i]] then + filesystemTreeUpdateFileListRecursively(tree, path .. expandables[i], offset + 2) + end + end + end + + if tree.showMode == GUI.IO_MODE_BOTH or tree.showMode == GUI.IO_MODE_FILE then + for i = 1, #list do + tree:addItem(list[i], path .. list[i], offset, false, tree.extensionFilters and not tree.extensionFilters[filesystem.extension(path .. list[i], true)] or false) + end + end +end + +local function filesystemTreeUpdateFileList(tree) + tree.items = {} + filesystemTreeUpdateFileListRecursively(tree, tree.workPath, 1) +end + +local function filesystemTreeAddExtensionFilter(tree, extensionFilter) + tree.extensionFilters = tree.extensionFilters or {} + tree.extensionFilters[unicode.lower(extensionFilter)] = true +end + +local function filesystemTreeExpandPath(tree, path) + local blyadina = tree.workPath + for pizda in path:gmatch("[^/]+") do + blyadina = blyadina .. pizda .. "/" + tree.expandedItems[blyadina] = true + end +end + +function GUI.filesystemTree(...) + local tree = GUI.tree(...) + + tree.workPath = "/" + tree.updateFileList = filesystemTreeUpdateFileList + tree.addExtensionFilter = filesystemTreeAddExtensionFilter + tree.expandPath = filesystemTreeExpandPath + tree.onItemExpanded = function() + tree:updateFileList() + end + + return tree +end + +-------------------------------------------------------------------------------- + +local function textBoxUpdate(object) + local doubleVerticalOffset = object.offset.vertical * 2 + object.textWidth = object.width - object.offset.horizontal * 2 - (object.scrollBarEnabled and 1 or 0) + + object.linesCopy = {} + + if object.autoWrap then + for i = 1, #object.lines do + local isTable = type(object.lines[i]) == "table" + for subLine in (isTable and object.lines[i].text or object.lines[i]):gmatch("[^\n]+") do + local wrappedLine = text.wrap(subLine, object.textWidth) + for j = 1, #wrappedLine do + table.insert(object.linesCopy, isTable and {text = wrappedLine[j], color = object.lines[i].color} or wrappedLine[j]) + end + end + end + else + for i = 1, #object.lines do + table.insert(object.linesCopy, object.lines[i]) + end + end + + if object.autoHeight then + object.height = #object.linesCopy + doubleVerticalOffset + end + + object.textHeight = object.height - doubleVerticalOffset +end + +local function textBoxDraw(object) + object:update() + + if object.colors.background then + screen.drawRectangle(object.x, object.y, object.width, object.height, object.colors.background, object.colors.text, " ", object.colors.transparency) + end + + local x, y = nil, object.y + object.offset.vertical + local lineType, line, textColor + for i = object.currentLine, object.currentLine + object.textHeight - 1 do + if object.linesCopy[i] then + lineType = type(object.linesCopy[i]) + if lineType == "string" then + line, textColor = text.limit(object.linesCopy[i], object.textWidth), object.colors.text + elseif lineType == "table" then + line, textColor = text.limit(object.linesCopy[i].text, object.textWidth), object.linesCopy[i].color + else + error("Unknown TextBox line type: " .. tostring(lineType)) + end + + x = GUI.getAlignmentCoordinates( + object.x + object.offset.horizontal, + 1, + object.textWidth, + 1, + object.horizontalAlignment, + object.verticalAlignment, + unicode.len(line), + 1 + ) + + screen.drawText(math.floor(x), y, textColor, line) + y = y + 1 + else + break + end + end + + if object.scrollBarEnabled and object.textHeight < #object.lines then + object.scrollBar.x = object.x + object.width - 1 + object.scrollBar.y = object.y + object.scrollBar.height = object.height + object.scrollBar.maximumValue = #object.lines - object.textHeight + 1 + object.scrollBar.value = object.currentLine + object.scrollBar.shownValueCount = object.textHeight + + object.scrollBar:draw() + end + + return object +end + +local function scrollDownTextBox(object, count) + count = math.min(count or 1, #object.lines - object.height - object.currentLine + object.offset.vertical * 2 + 1) + if #object.lines >= object.height and object.currentLine < #object.lines - count then + object.currentLine = object.currentLine + count + end + + return object +end + +local function scrollUpTextBox(object, count) + count = count or 1 + if object.currentLine > count and object.currentLine >= 1 then object.currentLine = object.currentLine - count end + return object +end + +local function scrollToStartTextBox(object) + object.currentLine = 1 + return object +end + +local function scrollToEndTextBox(object) + if #object.lines > object.textHeight then + object.currentLine = #object.lines - object.textHeight + 1 + end + + return object +end + +local function textBoxScrollEventHandler(workspace, object, e1, e2, e3, e4, e5) + if e1 == "scroll" then + if e5 == 1 then + object:scrollUp() + else + object:scrollDown() + end + + workspace:draw() + end +end + +function GUI.textBox(x, y, width, height, backgroundColor, textColor, lines, currentLine, horizontalOffset, verticalOffset, autoWrap, autoHeight) + local object = GUI.object(x, y, width, height) + + object.colors = { + text = textColor, + background = backgroundColor + } + object.lines = lines + object.currentLine = currentLine or 1 + object.scrollUp = scrollUpTextBox + object.scrollDown = scrollDownTextBox + object.scrollToStart = scrollToStartTextBox + object.scrollToEnd = scrollToEndTextBox + object.offset = {horizontal = horizontalOffset or 0, vertical = verticalOffset or 0} + object.autoWrap = autoWrap + object.autoHeight = autoHeight + object.scrollBar = GUI.scrollBar(1, 1, 1, 1, 0xC3C3C3, 0x4B4B4B, 1, 1, 1, 1, 1, true) + object.scrollBarEnabled = false + + object.eventHandler = textBoxScrollEventHandler + object.draw = textBoxDraw + object.update = textBoxUpdate + + object.setAlignment = GUI.setAlignment + object:setAlignment(GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP) + object:update() + + return object +end + +-------------------------------------------------------------------------------- + +local function inputSetCursorPosition(input, newPosition) + if newPosition < 1 then + newPosition = 1 + elseif newPosition > unicode.len(input.text) + 1 then + newPosition = unicode.len(input.text) + 1 + end + + if newPosition > input.textCutFrom + input.width - 1 - input.textOffset * 2 then + input.textCutFrom = input.textCutFrom + newPosition - (input.textCutFrom + input.width - 1 - input.textOffset * 2) + elseif newPosition < input.textCutFrom then + input.textCutFrom = newPosition + end + + input.cursorPosition = newPosition + + return input +end + +local function inputTextDrawMethod(x, y, color, text) + screen.drawText(x, y, color, text) +end + +local function inputDraw(input) + local background, foreground, transparency, text + if input.focused then + background, transparency = input.colors.focused.background, input.colors.focused.transparency + if input.text == "" then + input.textCutFrom = 1 + foreground, text = input.colors.placeholderText, input.text + else + foreground = input.colors.focused.text + if input.textMask then + text = string.rep(input.textMask, unicode.len(input.text)) + else + text = input.text + end + end + else + background, transparency = input.colors.default.background, input.colors.default.transparency + if input.text == "" then + input.textCutFrom = 1 + foreground, text = input.colors.placeholderText, input.placeholderText + else + foreground = input.colors.default.text + if input.textMask then + text = string.rep(input.textMask, unicode.len(input.text)) + else + text = input.text + end + end + end + + if background then + screen.drawRectangle(input.x, input.y, input.width, input.height, background, foreground, " ", transparency) + end + + local y = input.y + math.floor(input.height / 2) + + input.textDrawMethod( + input.x + input.textOffset, + y, + foreground, + unicode.sub( + text or "", + input.textCutFrom, + input.textCutFrom + input.width - 1 - input.textOffset * 2 + ) + ) + + if input.cursorBlinkState then + local index = screen.getIndex(input.x + input.cursorPosition - input.textCutFrom + input.textOffset, y) + local background = screen.rawGet(index) + screen.rawSet(index, background, input.colors.cursor, input.cursorSymbol) + end +end + +local function inputCursorBlink(workspace, input, state) + input.cursorBlinkState = state + input.cursorBlinkUptime = computer.uptime() + workspace:draw() +end + +local function inputStopInput(workspace, input) + input.stopInputObject:remove() + input.focused = false + + if input.validator then + if not input.validator(input.text) then + input.text = input.startText + input.startText = nil + + input:setCursorPosition(unicode.len(input.text) + 1) + end + end + + if input.onInputFinished then + input.onInputFinished(workspace, input) + end + + inputCursorBlink(workspace, input, false) +end + +local function inputStartInput(input) + input.startText = input.text + input.focused = true + + if input.historyEnabled then + input.historyIndex = input.historyIndex + 1 + end + + if input.eraseTextOnFocus then + input.text = "" + end + + input:setCursorPosition(unicode.len(input.text) + 1) + + input.stopInputObject.width, input.stopInputObject.height = input.firstParent.width, input.firstParent.height + input.firstParent:addChild(input.stopInputObject) + + inputCursorBlink(input.firstParent, input, true) +end + +local function inputEventHandler(workspace, input, e1, e2, e3, e4, e5, e6) + if e1 == "touch" or e1 == "drag" then + if input.focused then + input:setCursorPosition(input.textCutFrom + e3 - input.x - input.textOffset) + inputCursorBlink(workspace, input, true) + else + input:startInput() + end + elseif e1 == "key_down" and input.focused then + workspace:consumeEvent() + + -- Return + if e4 == 28 then + if input.historyEnabled then + -- Очистка истории + for i = 1, (#input.history - input.historyLimit) do + table.remove(input.history, 1) + end + + -- Добавление введенных данных в историю + if input.history[#input.history] ~= input.text and unicode.len(input.text) > 0 then + table.insert(input.history, input.text) + end + input.historyIndex = #input.history + end + + inputStopInput(workspace, input) + return + -- Arrows up/down/left/right + elseif e4 == 200 then + if input.historyEnabled and #input.history > 0 then + -- Добавление уже введенного текста в историю при стрелке вверх + if input.historyIndex == #input.history + 1 and unicode.len(input.text) > 0 then + input.history[input.historyIndex] = input.text + end + + input.historyIndex = input.historyIndex - 1 + if input.historyIndex > #input.history then + input.historyIndex = #input.history + elseif input.historyIndex < 1 then + input.historyIndex = 1 + end + + input.text = input.history[input.historyIndex] + input:setCursorPosition(unicode.len(input.text) + 1) + end + elseif e4 == 208 then + if input.historyEnabled and #input.history > 0 then + input.historyIndex = input.historyIndex + 1 + if input.historyIndex > #input.history then + input.historyIndex = #input.history + elseif input.historyIndex < 1 then + input.historyIndex = 1 + end + + input.text = input.history[input.historyIndex] + input:setCursorPosition(unicode.len(input.text) + 1) + end + elseif e4 == 203 then + input:setCursorPosition(input.cursorPosition - 1) + elseif e4 == 205 then + input:setCursorPosition(input.cursorPosition + 1) + -- Backspace + elseif e4 == 14 then + input.text = unicode.sub(unicode.sub(input.text, 1, input.cursorPosition - 1), 1, -2) .. unicode.sub(input.text, input.cursorPosition, -1) + input:setCursorPosition(input.cursorPosition - 1) + -- Delete + elseif e4 == 211 then + input.text = unicode.sub(input.text, 1, input.cursorPosition - 1) .. unicode.sub(input.text, input.cursorPosition + 1, -1) + else + local char = unicode.char(e3) + if not keyboard.isControl(e3) then + input.text = unicode.sub(input.text, 1, input.cursorPosition - 1) .. char .. unicode.sub(input.text, input.cursorPosition, -1) + input:setCursorPosition(input.cursorPosition + 1) + end + end + + inputCursorBlink(workspace, input, true) + elseif e1 == "clipboard" and input.focused then + input.text = unicode.sub(input.text, 1, input.cursorPosition - 1) .. e3 .. unicode.sub(input.text, input.cursorPosition, -1) + input:setCursorPosition(input.cursorPosition + unicode.len(e3)) + + inputCursorBlink(workspace, input, true) + workspace:consumeEvent() + elseif not e1 and input.focused and computer.uptime() - input.cursorBlinkUptime > input.cursorBlinkDelay then + inputCursorBlink(workspace, input, not input.cursorBlinkState) + end +end + +function GUI.input(x, y, width, height, backgroundColor, textColor, placeholderTextColor, backgroundFocusedColor, textFocusedColor, text, placeholderText, eraseTextOnFocus, textMask) + local input = GUI.object(x, y, width, height) + + input.colors = { + default = { + background = backgroundColor, + text = textColor + }, + focused = { + background = backgroundFocusedColor, + text = textFocusedColor + }, + placeholderText = placeholderTextColor, + cursor = 0x00A8FF + } + + input.text = text or "" + input.placeholderText = placeholderText + input.eraseTextOnFocus = eraseTextOnFocus + input.textMask = textMask + + input.textOffset = 1 + input.textCutFrom = 1 + input.cursorPosition = 1 + input.cursorSymbol = "┃" + input.cursorBlinkDelay = 0.4 + input.cursorBlinkState = false + input.textMask = textMask + input.setCursorPosition = inputSetCursorPosition + + input.history = {} + input.historyLimit = 20 + input.historyIndex = 0 + input.historyEnabled = false + + input.stopInputObject = GUI.object(1, 1, 1, 1) + input.stopInputObject.eventHandler = function(workspace, object, e1, e2, e3, e4, ...) + if e1 == "touch" or e1 == "drop" then + if input:isPointInside(e3, e4) then + input.eventHandler(workspace, input, e1, e2, e3, e4, ...) + else + inputStopInput(workspace, input) + end + end + end + + input.textDrawMethod = inputTextDrawMethod + input.draw = inputDraw + input.eventHandler = inputEventHandler + input.startInput = inputStartInput + + return input +end + +-------------------------------------------------------------------------------- + +local function autoCompleteDraw(object) + local y, yEnd = object.y, object.y + object.height - 1 + + screen.drawRectangle(object.x, object.y, object.width, object.height, object.colors.default.background, object.colors.default.text, " ") + + for i = object.fromItem, object.itemCount do + local textColor, textMatchColor = object.colors.default.text, object.colors.default.textMatch + if i == object.selectedItem then + screen.drawRectangle(object.x, y, object.width, 1, object.colors.selected.background, object.colors.selected.text, " ") + textColor, textMatchColor = object.colors.selected.text, object.colors.selected.textMatch + end + + screen.drawText(object.x + 1, y, textMatchColor, unicode.sub(object.matchText, 1, object.width - 2)) + screen.drawText(object.x + object.matchTextLength + 1, y, textColor, unicode.sub(object.items[i], object.matchTextLength + 1, object.matchTextLength + object.width - object.matchTextLength - 2)) + + y = y + 1 + if y > yEnd then + break + end + end + + if object.itemCount > object.height then + object.scrollBar.x = object.x + object.width - 1 + object.scrollBar.y = object.y + object.scrollBar.height = object.height + object.scrollBar.maximumValue = object.itemCount - object.height + 2 + object.scrollBar.value = object.fromItem + object.scrollBar.shownValueCount = object.height + + object.scrollBar:draw() + end +end + +local function autoCompleteScroll(workspace, object, direction) + if object.itemCount >= object.height then + object.fromItem = object.fromItem + direction + if object.fromItem < 1 then + object.fromItem = 1 + elseif object.fromItem > object.itemCount - object.height + 1 then + object.fromItem = object.itemCount - object.height + 1 + end + end +end + +local function autoCompleteEventHandler(workspace, object, e1, e2, e3, e4, e5, ...) + if e1 == "touch" then + object.selectedItem = e4 - object.y + object.fromItem + workspace:draw() + + if object.onItemSelected then + event.sleep(0.2) + object.onItemSelected(workspace, object, e1, e2, e3, e4, e5, ...) + end + elseif e1 == "scroll" then + autoCompleteScroll(workspace, object, -e5) + workspace:draw() + elseif e1 == "key_down" then + if e4 == 28 then + if object.onItemSelected then + object.onItemSelected(workspace, object, e1, e2, e3, e4, e5, ...) + end + elseif e4 == 200 then + object.selectedItem = object.selectedItem - 1 + if object.selectedItem < 1 then + object.selectedItem = 1 + end + + if object.selectedItem == object.fromItem - 1 then + autoCompleteScroll(workspace, object, -1) + end + + workspace:draw() + elseif e4 == 208 then + object.selectedItem = object.selectedItem + 1 + if object.selectedItem > object.itemCount then + object.selectedItem = object.itemCount + end + + if object.selectedItem == object.fromItem + object.height then + autoCompleteScroll(workspace, object, 1) + end + + workspace:draw() + end + end +end + +local function autoCompleteClear(object) + object.items = {} + object.itemCount = 0 + object.fromItem = 1 + object.selectedItem = 1 + object.height = 0 +end + +local function autoCompleteMatch(object, variants, text, asKey) + object:clear() + + if asKey then + if text then + for key in pairs(variants) do + if key ~= text and key:match("^" .. text) then + table.insert(object.items,key) + end + end + else + for key in pairs(variants) do + table.insert(object.items, key) + end + end + else + if text then + for i = 1, #variants do + if variants[i] ~= text and variants[i]:match("^" .. text) then + table.insert(object.items, variants[i]) + end + end + else + for i = 1, #variants do + table.insert(object.items, variants[i]) + end + end + end + + object.matchText = text or "" + object.matchTextLength = unicode.len(object.matchText) + + table.sort(object.items, function(a, b) return unicode.lower(a) < unicode.lower(b) end) + + object.itemCount = #object.items + object.height = math.min(object.itemCount, object.maximumHeight) + + return object +end + +function GUI.autoComplete(x, y, width, maximumHeight, backgroundColor, textColor, textMatchColor, backgroundSelectedColor, textSelectedColor, textMatchSelectedColor, scrollBarBackground, scrollBarForeground) + local object = GUI.object(x, y, width, maximumHeight) + + object.colors = { + default = { + background = backgroundColor, + text = textColor, + textMatch = textMatchColor + }, + selected = { + background = backgroundSelectedColor, + text = textSelectedColor, + textMatch = textMatchSelectedColor + } + } + + object.maximumHeight = maximumHeight + object.fromItem = 1 + object.selectedItem = 1 + object.items = {} + object.matchText = " " + object.matchTextLength = 1 + object.itemCount = 0 + + object.scrollBar = GUI.scrollBar(1, 1, 1, 1, scrollBarBackground, scrollBarForeground, 1, 1, 1, 1, 1, true) + + object.match = autoCompleteMatch + object.draw = autoCompleteDraw + object.eventHandler = autoCompleteEventHandler + object.clear = autoCompleteClear + + object:clear() + + return object +end + +-------------------------------------------------------------------------------- + +local function brailleCanvasDraw(brailleCanvas) + local index, background, foreground, symbol + for y = 1, brailleCanvas.height do + for x = 1, brailleCanvas.width do + index = screen.getIndex(brailleCanvas.x + x - 1, brailleCanvas.y + y - 1) + background, foreground, symbol = screen.rawGet(index) + screen.rawSet(index, background, brailleCanvas.pixels[y][x][9], brailleCanvas.pixels[y][x][10]) + end + end + + return brailleCanvas +end + +local function brailleCanvasSet(brailleCanvas, x, y, state, color) + local xReal, yReal = math.ceil(x / 2), math.ceil(y / 4) + + brailleCanvas.pixels[yReal][xReal][(y - (yReal - 1) * 4 - 1) * 2 + x - (xReal - 1) * 2] = state and 1 or 0 + brailleCanvas.pixels[yReal][xReal][9] = color or brailleCanvas.pixels[yReal][xReal][9] + brailleCanvas.pixels[yReal][xReal][10] = unicode.char( + 10240 + + 128 * brailleCanvas.pixels[yReal][xReal][8] + + 64 * brailleCanvas.pixels[yReal][xReal][7] + + 32 * brailleCanvas.pixels[yReal][xReal][6] + + 16 * brailleCanvas.pixels[yReal][xReal][4] + + 8 * brailleCanvas.pixels[yReal][xReal][2] + + 4 * brailleCanvas.pixels[yReal][xReal][5] + + 2 * brailleCanvas.pixels[yReal][xReal][3] + + brailleCanvas.pixels[yReal][xReal][1] + ) + + return brailleCanvas +end + +local function brailleCanvasGet(brailleCanvas, x, y) + local xReal, yReal = math.ceil(x / 2), math.ceil(y / 4) + return brailleCanvas.pixels[yReal][xReal][(y - (yReal - 1) * 4 - 1) * 2 + x - (xReal - 1) * 2], brailleCanvas.pixels[yReal][xReal][9], brailleCanvas.pixels[yReal][xReal][10] +end + +local function brailleCanvasFill(brailleCanvas, x, y, width, height, state, color) + for j = y, y + height - 1 do + for i = x, x + width - 1 do + brailleCanvas:set(i, j, state, color) + end + end +end + +local function brailleCanvasClear(brailleCanvas) + for j = 1, brailleCanvas.height * 4 do + brailleCanvas.pixels[j] = {} + for i = 1, brailleCanvas.width * 2 do + brailleCanvas.pixels[j][i] = { 0, 0, 0, 0, 0, 0, 0, 0, 0x0, " " } + end + end +end + +function GUI.brailleCanvas(x, y, width, height) + local brailleCanvas = GUI.object(x, y, width, height) + + brailleCanvas.pixels = {} + + brailleCanvas.get = brailleCanvasGet + brailleCanvas.set = brailleCanvasSet + brailleCanvas.fill = brailleCanvasFill + brailleCanvas.clear = brailleCanvasClear + + brailleCanvas.draw = brailleCanvasDraw + + brailleCanvas:clear() + + return brailleCanvas +end + +-------------------------------------------------------------------------------- + +local function paletteShow(palette) + local workspace = GUI.workspace() + + workspace:addChild(palette) + + palette.submitButton.onTouch = function() + workspace:stop() + end + palette.cancelButton.onTouch = palette.submitButton.onTouch + + workspace:draw() + workspace:start() + + return palette.color.integer +end + +function GUI.palette(x, y, startColor) + local palette = GUI.window(x, y, 71, 25) + + palette.color = {hsb = {}, rgb = {}} + palette:addChild(GUI.panel(1, 1, palette.width, palette.height, 0xF0F0F0)) + + local bigImage = palette:addChild(GUI.image(1, 1, image.create(50, 25))) + local bigCrest = palette:addChild(GUI.object(1, 1, 5, 3)) + + local function paletteDrawBigCrestPixel(x, y, symbol) + local background, foreground = screen.get(x, y) + local r, g, b = color.integerToRGB(background) + screen.set(x, y, background, (r + g + b) / 3 >= 127 and 0x0 or 0xFFFFFF, symbol) + end + + bigCrest.draw = function(object) + paletteDrawBigCrestPixel(object.x, object.y + 1, "─") + paletteDrawBigCrestPixel(object.x + 1, object.y + 1, "─") + paletteDrawBigCrestPixel(object.x + 3, object.y + 1, "─") + paletteDrawBigCrestPixel(object.x + 4, object.y + 1, "─") + paletteDrawBigCrestPixel(object.x + 2, object.y, "│") + paletteDrawBigCrestPixel(object.x + 2, object.y + 2, "│") + end + bigCrest.passScreenEvents = true + + local miniImage = palette:addChild(GUI.image(53, 1, image.create(3, 25))) + + local miniCrest = palette:addChild(GUI.object(52, 1, 5, 1)) + miniCrest.draw = function(object) + screen.drawText(object.x, object.y, 0x0, ">") + screen.drawText(object.x + 4, object.y, 0x0, "<") + end + + local colorPanel = palette:addChild(GUI.panel(58, 2, 12, 3, 0x0)) + palette.submitButton = palette:addChild(GUI.roundedButton(58, 6, 12, 1, 0x4B4B4B, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, "OK")) + palette.cancelButton = palette:addChild(GUI.roundedButton(58, 8, 12, 1, 0xFFFFFF, 0x696969, 0x2D2D2D, 0xFFFFFF, "Cancel")) + + local function paletteRefreshBigImage() + local saturationStep, brightnessStep, saturation, brightness = 1 / bigImage.width, 1 / bigImage.height, 0, 1 + for j = 1, bigImage.height do + for i = 1, bigImage.width do + image.set(bigImage.image, i, j, color.optimize(color.HSBToInteger(palette.color.hsb.hue, saturation, brightness)), 0x0, 0x0, " ") + saturation = saturation + saturationStep + end + saturation, brightness = 0, brightness - brightnessStep + end + end + + local function paletteRefreshMiniImage() + local hueStep, hue = 360 / miniImage.height, 0 + for j = 1, miniImage.height do + for i = 1, miniImage.width do + image.set(miniImage.image, i, j, color.optimize(color.HSBToInteger(hue, 1, 1)), 0x0, 0, " ") + end + hue = hue + hueStep + end + end + + local function paletteUpdateCrestsCoordinates() + bigCrest.localX = math.floor((bigImage.width - 1) * palette.color.hsb.saturation) - 1 + bigCrest.localY = math.floor((bigImage.height - 1) - (bigImage.height - 1) * palette.color.hsb.brightness) + miniCrest.localY = math.ceil(palette.color.hsb.hue / 360 * miniImage.height + 0.5) + end + + local inputs + + local function paletteUpdateInputs() + inputs[1].text = tostring(palette.color.rgb.red) + inputs[2].text = tostring(palette.color.rgb.green) + inputs[3].text = tostring(palette.color.rgb.blue) + inputs[4].text = tostring(math.floor(palette.color.hsb.hue)) + inputs[5].text = tostring(math.floor(palette.color.hsb.saturation * 100)) + inputs[6].text = tostring(math.floor(palette.color.hsb.brightness * 100)) + inputs[7].text = string.format("%06X", palette.color.integer) + colorPanel.colors.background = palette.color.integer + end + + local function paletteSwitchColorFromHex(hex) + palette.color.integer = hex + palette.color.rgb.red, palette.color.rgb.green, palette.color.rgb.blue = color.integerToRGB(hex) + palette.color.hsb.hue, palette.color.hsb.saturation, palette.color.hsb.brightness = color.RGBToHSB(palette.color.rgb.red, palette.color.rgb.green, palette.color.rgb.blue) + paletteUpdateInputs() + end + + local function paletteSwitchColorFromHsb(hue, saturation, brightness) + palette.color.hsb.hue, palette.color.hsb.saturation, palette.color.hsb.brightness = hue, saturation, brightness + palette.color.rgb.red, palette.color.rgb.green, palette.color.rgb.blue = color.HSBToRGB(hue, saturation, brightness) + palette.color.integer = color.RGBToInteger(palette.color.rgb.red, palette.color.rgb.green, palette.color.rgb.blue) + paletteUpdateInputs() + end + + local function paletteSwitchColorFromRgb(red, green, blue) + palette.color.rgb.red, palette.color.rgb.green, palette.color.rgb.blue = red, green, blue + palette.color.hsb.hue, palette.color.hsb.saturation, palette.color.hsb.brightness = color.RGBToHSB(red, green, blue) + palette.color.integer = color.RGBToInteger(red, green, blue) + paletteUpdateInputs() + end + + local function onAnyInputFinished() + paletteRefreshBigImage() + paletteUpdateCrestsCoordinates() + palette.firstParent:draw() + end + + local function onHexInputFinished() + paletteSwitchColorFromHex(tonumber("0x" .. inputs[7].text)) + onAnyInputFinished() + end + + local function onRgbInputFinished() + paletteSwitchColorFromRgb(tonumber(inputs[1].text), tonumber(inputs[2].text), tonumber(inputs[3].text)) + onAnyInputFinished() + end + + local function onHsbInputFinished() + paletteSwitchColorFromHsb(tonumber(inputs[4].text), tonumber(inputs[5].text) / 100, tonumber(inputs[6].text) / 100) + onAnyInputFinished() + end + + local function rgbValidaror(text) + local number = tonumber(text) if number and number >= 0 and number <= 255 then return true end + end + + local function hValidator(text) + local number = tonumber(text) if number and number >= 0 and number <= 359 then return true end + end + + local function sbValidator(text) + local number = tonumber(text) if number and number >= 0 and number <= 100 then return true end + end + + local function hexValidator(text) + if string.match(text, "^[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]$") then + return true + end + end + + inputs = { + { shortcut = "R:", validator = rgbValidaror, onInputFinished = onRgbInputFinished }, + { shortcut = "G:", validator = rgbValidaror, onInputFinished = onRgbInputFinished }, + { shortcut = "B:", validator = rgbValidaror, onInputFinished = onRgbInputFinished }, + { shortcut = "H:", validator = hValidator, onInputFinished = onHsbInputFinished }, + { shortcut = "S:", validator = sbValidator, onInputFinished = onHsbInputFinished }, + { shortcut = "L:", validator = sbValidator, onInputFinished = onHsbInputFinished }, + { shortcut = "0x", validator = hexValidator, onInputFinished = onHexInputFinished } + } + + local y = 10 + for i = 1, #inputs do + palette:addChild(GUI.label(58, y, 2, 1, 0x0, inputs[i].shortcut)) + + local validator, onInputFinished = inputs[i].validator, inputs[i].onInputFinished + inputs[i] = palette:addChild(GUI.input(61, y, 9, 1, 0xFFFFFF, 0x696969, 0x696969, 0xFFFFFF, 0x0, "", "", true)) + inputs[i].validator = validator + inputs[i].onInputFinished = onInputFinished + + y = y + 2 + end + + local favourites + if filesystem.exists(GUI.PALETTE_CONFIG_PATH) then + favourites = filesystem.readTable(GUI.PALETTE_CONFIG_PATH) + else + favourites = {} + for i = 1, 6 do favourites[i] = color.HSBToInteger(math.random(0, 360), 1, 1) end + filesystem.writeTable(GUI.PALETTE_CONFIG_PATH, favourites) + end + + local favouritesContainer = palette:addChild(GUI.container(58, 24, 12, 1)) + for i = 1, #favourites do + favouritesContainer:addChild(GUI.button(i * 2 - 1, 1, 2, 1, favourites[i], 0x0, 0x0, 0x0, " ")).onTouch = function(workspace) + paletteSwitchColorFromHex(favourites[i]) + paletteRefreshBigImage() + paletteUpdateCrestsCoordinates() + workspace:draw() + end + end + + palette:addChild(GUI.button(58, 25, 12, 1, 0xFFFFFF, 0x4B4B4B, 0x2D2D2D, 0xFFFFFF, "+")).onTouch = function(workspace) + local favouriteExists = false + for i = 1, #favourites do + if favourites[i] == palette.color.integer then + favouriteExists = true + break + end + end + + if not favouriteExists then + table.insert(favourites, 1, palette.color.integer) + table.remove(favourites, #favourites) + for i = 1, #favourites do + favouritesContainer.children[i].colors.default.background = favourites[i] + favouritesContainer.children[i].colors.pressed.background = 0x0 + end + + filesystem.writeTable(GUI.PALETTE_CONFIG_PATH, favourites) + + workspace:draw() + end + end + + bigImage.eventHandler = function(workspace, object, e1, e2, e3, e4) + if e1 == "touch" or e1 == "drag" then + bigCrest.localX, bigCrest.localY = e3 - palette.x - 1, e4 - palette.y + paletteSwitchColorFromHex(select(3, screen.getGPUProxy().get(e3, e4))) + workspace:draw() + end + end + + miniImage.eventHandler = function(workspace, object, e1, e2, e3, e4) + if e1 == "touch" or e1 == "drag" then + miniCrest.localY = e4 - palette.y + 1 + paletteSwitchColorFromHsb((e4 - miniImage.y) * 360 / miniImage.height, palette.color.hsb.saturation, palette.color.hsb.brightness) + paletteRefreshBigImage() + workspace:draw() + end + end + + palette.show = paletteShow + + paletteSwitchColorFromHex(startColor) + paletteUpdateCrestsCoordinates() + paletteRefreshBigImage() + paletteRefreshMiniImage() + + return palette +end + +-------------------------------------------------------------------------------- + +local function textUpdate(object) + object.width = unicode.len(object.text) + return object +end + +local function textDraw(object) + object:update() + screen.drawText(object.x, object.y, object.color, object.text) + return object +end + +function GUI.text(x, y, color, text) + local object = GUI.object(x, y, 1, 1) + + object.text = text + object.color = color + object.update = textUpdate + object.draw = textDraw + object:update() + + return object +end + +-------------------------------------------------------------------------------- + +function GUI.addBackgroundContainer(parentContainer, addPanel, addLayout, title) + local container = parentContainer:addChild(GUI.container(1, 1, parentContainer.width, parentContainer.height)) + + if addPanel then + container.panel = container:addChild(GUI.panel(1, 1, container.width, container.height, GUI.BACKGROUND_CONTAINER_PANEL_COLOR, GUI.BACKGROUND_CONTAINER_PANEL_TRANSPARENCY)) + container.panel.eventHandler = function(parentContainer, object, e1) + if e1 == "touch" then + container:remove() + parentContainer:draw() + end + end + end + + if addLayout then + container.layout = container:addChild(GUI.layout(1, 1, container.width, container.height, 1, 1)) + + if title then + container.label = container.layout:addChild(GUI.label(1, 1, 1, 1, GUI.BACKGROUND_CONTAINER_TITLE_COLOR, title)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + end + end + + return container +end + +-------------------------------------------------------------------------------- + +local function listUpdate(list) + local step, child = false + for i = 1, #list.children do + child = list.children[i] + -- Жмяканье пизды + child.pressed = i == list.selectedItem + + -- Цвет залупы + if step then + child.colors.default = list.colors.alternative + else + child.colors.default = list.colors.default + end + child.colors.pressed, step = list.colors.selected, not step + + -- Размеры хуйни + if list.cells[1][1].direction == GUI.DIRECTION_HORIZONTAL then + if list.offsetMode then + child.width, child.height = list.itemSize * 2 + unicode.len(child.text), list.height + else + child.width, child.height = list.itemSize, list.height + end + else + if list.offsetMode then + child.width, child.height = list.width, list.itemSize * 2 + 1 + else + child.width, child.height = list.width, list.itemSize + end + end + end + + layoutUpdate(list) +end + +local function listItemEventHandler(workspace, item, e1, ...) + if e1 == "touch" or e1 == "drag" then + item.parent.selectedItem = item:indexOf() + item.parent:update() + workspace:draw() + + if item.onTouch then + item.onTouch(workspace, item, e1, ...) + end + end +end + +local function listAddItem(list, text) + local item = list:addChild(pressable(1, 1, 1, 1, 0, 0, 0, 0, 0, 0, text)) + + item.switchMode = true + item.eventHandler = listItemEventHandler + + return item +end + +local function listSetAlignment(list, ...) + layoutSetAlignment(list, 1, 1, ...) + return list +end + +local function listSetSpacing(list, ...) + layoutSetSpacing(list, 1, 1, ...) + return list +end + +local function listSetDirection(list, ...) + layoutSetDirection(list, 1, 1, ...) + return list +end + +local function listSetFitting(list, ...) + layoutSetFitting(list, 1, 1, ...) + return list +end + +local function listSetMargin(list, ...) + layoutSetMargin(list, 1, 1, ...) + return list +end + +local function listGetMargin(list, ...) + return layoutGetMargin(list, 1, 1, ...) +end + +local function listGetItem(list, what) + if type(what) == "number" then + return list.children[what] + else + for i = 1, #list.children do + if list.children[i].text == what then + return list.children[i], i + end + end + end +end + +local function listCount(list) + return #list.children +end + +local function listDraw(list) + screen.drawRectangle(list.x, list.y, list.width, list.height, list.colors.default.background, list.colors.default.text, " ") + layoutDraw(list) +end + +function GUI.list(x, y, width, height, itemSize, spacing, backgroundColor, textColor, backgroundAlternatingColor, textAlternatingColor, backgroundSelectedColor, textSelectedColor, offsetMode) + local list = GUI.layout(x, y, width, height, 1, 1) + + list.colors = { + default = { + background = backgroundColor, + text = textColor + }, + alternative = { + background = backgroundAlternatingColor, + text = textAlternatingColor + }, + selected = { + background = backgroundSelectedColor, + text = textSelectedColor + }, + } + + list.passScreenEvents = false + list.selectedItem = 1 + list.offsetMode = offsetMode + list.itemSize = itemSize + + list.addItem = listAddItem + list.getItem = listGetItem + list.count = listCount + list.setAlignment = listSetAlignment + list.setSpacing = listSetSpacing + list.setDirection = listSetDirection + list.setFitting = listSetFitting + list.setMargin = listSetMargin + list.getMargin = listGetMargin + list.update = listUpdate + list.draw = listDraw + + list:setAlignment(GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP) + list:setSpacing(spacing) + list:setDirection(GUI.DIRECTION_VERTICAL) + + return list +end + +--------------------------------------------------------------------------------------------------- + +local function keyAndValueUpdate(object) + object.keyLength, object.valueLength = unicode.len(object.key), unicode.len(object.value) + object.width = object.keyLength + object.valueLength +end + +local function keyAndValueDraw(object) + keyAndValueUpdate(object) + screen.drawText(object.x, object.y, object.colors.key, object.key) + screen.drawText(object.x + object.keyLength, object.y, object.colors.value, object.value) +end + +function GUI.keyAndValue(x, y, keyColor, valueColor, key, value) + local object = GUI.object(x, y, 1, 1) + + object.colors = { + key = keyColor, + value = valueColor + } + object.key = key + object.value = value + + object.update = keyAndValueUpdate + object.draw = keyAndValueDraw + + object:update() + + return object +end + +--------------------------------------------------------------------------------------------------- + +function GUI.highlightString(x, y, width, fromChar, indentationWidth, patterns, colorScheme, s) + fromChar = fromChar or 1 + + local counter, symbols, colors, stringLength, bufferIndex, newFrameBackgrounds, newFrameForegrounds, newFrameSymbols, searchFrom, starting, ending = indentationWidth, {}, {}, unicode.len(s), screen.getIndex(x, y), screen.getNewFrameTables() + local toChar = math.min(stringLength, fromChar + width - 1) + + -- Пидорасим на символы + for i = fromChar, toChar do + symbols[i] = unicode.sub(s, i, i) + end + + -- Вгоняем в цветовую карту синтаксическую подсветку + for j = 1, #patterns, 4 do + searchFrom = 1 + + while true do + starting, ending = text.unicodeFind(s, patterns[j], searchFrom) + + if starting then + for i = starting + patterns[j + 2], ending - patterns[j + 3] do + colors[i] = colorScheme[patterns[j + 1]] + end + else + break + end + + searchFrom = ending + 1 - patterns[j + 3] + end + end + + -- Ебошим индентейшны + for i = fromChar, toChar do + if symbols[i] == " " then + colors[i] = colorScheme.indentation + + if counter == indentationWidth then + symbols[i], counter = "│", 0 + end + + counter = counter + 1 + else + break + end + end + + -- Рисуем текст + for i = fromChar, toChar do + newFrameForegrounds[bufferIndex], newFrameSymbols[bufferIndex] = colors[i] or colorScheme.text, symbols[i] or " " + bufferIndex = bufferIndex + 1 + end +end + +-------------------------------------------------------------------------------- + +local function dropDownMenuItemDraw(item) + local yText = item.y + math.floor(item.height / 2) + + if item.type == 1 then + local textColor = item.color or item.parent.parent.colors.default.text + + if item.pressed then + textColor = item.parent.parent.colors.selected.text + screen.drawRectangle(item.x, item.y, item.width, item.height, item.parent.parent.colors.selected.background, textColor, " ") + elseif item.disabled then + textColor = item.parent.parent.colors.disabled.text + end + + screen.drawText(item.x + 1, yText, textColor, item.text) + if item.shortcut then + screen.drawText(item.x + item.width - unicode.len(item.shortcut) - 1, yText, textColor, item.shortcut) + end + else + screen.drawText(item.x, yText, item.parent.parent.colors.separator, string.rep("─", item.width)) + end + + return item +end + +local function dropDownMenuReleaseItems(menu) + for i = 1, #menu.itemsContainer.children do + menu.itemsContainer.children[i].pressed = false + end + + return menu +end + +local function dropDownMenuItemEventHandler(workspace, object, e1, ...) + if e1 == "touch" then + if object.type == 1 and not object.pressed then + object.pressed = true + workspace:draw() + + if object.subMenu then + object.parent.parent.parent:addChild(object.subMenu:releaseItems()) + object.subMenu.localX = object.parent.parent.localX + object.parent.parent.width + object.subMenu.localY = object.parent.parent.localY + object.localY - 1 + if screen.getWidth() - object.parent.parent.localX - object.parent.parent.width + 1 < object.subMenu.width then + object.subMenu.localX = object.parent.parent.localX - object.subMenu.width + object.parent.parent:moveToFront() + end + + workspace:draw() + else + event.sleep(0.2) + + object.parent.parent.parent:remove() + + local objectIndex = object:indexOf() + for i = 2, #object.parent.parent.parent.children do + if object.parent.parent.parent.children[i].onMenuClosed then + object.parent.parent.parent.children[i].onMenuClosed(objectIndex) + end + end + + if object.onTouch then + object.onTouch() + end + + workspace:draw() + end + end + end +end + +local function dropDownMenuGetHeight(menu) + local height = 0 + for i = 1, #menu.itemsContainer.children do + height = height + (menu.itemsContainer.children[i].type == 2 and 1 or menu.itemHeight) + end + + return height +end + +local function dropDownMenuReposition(menu) + menu.itemsContainer.width, menu.itemsContainer.height = menu.width, menu.height + menu.prevButton.width, menu.nextButton.width = menu.width, menu.width + menu.nextButton.localY = menu.height + + local y = menu.itemsContainer.children[1].localY + for i = 1, #menu.itemsContainer.children do + menu.itemsContainer.children[i].localY = y + menu.itemsContainer.children[i].width = menu.itemsContainer.width + y = y + menu.itemsContainer.children[i].height + end + + menu.prevButton.hidden = menu.itemsContainer.children[1].localY >= 1 + menu.nextButton.hidden = menu.itemsContainer.children[#menu.itemsContainer.children].localY + menu.itemsContainer.children[#menu.itemsContainer.children].height - 1 <= menu.height +end + +local function dropDownMenuUpdate(menu) + if #menu.itemsContainer.children > 0 then + menu.height = math.min(dropDownMenuGetHeight(menu), menu.maximumHeight, screen.getHeight() - menu.y) + dropDownMenuReposition(menu) + end +end + +local function dropDownMenuRemoveItem(menu, index) + table.remove(menu.itemsContainer.children, index) + + menu:update() + + return menu +end + +local function dropDownMenuAddItem(menu, text, disabled, shortcut, color) + local item = menu.itemsContainer:addChild(GUI.object(1, 1, 1, menu.itemHeight)) + item.type = 1 + item.text = text + item.disabled = disabled + item.shortcut = shortcut + item.color = color + item.draw = dropDownMenuItemDraw + item.eventHandler = dropDownMenuItemEventHandler + + menu:update() + + return item +end + +local function dropDownMenuAddSeparator(menu) + local item = menu.itemsContainer:addChild(GUI.object(1, 1, 1, 1)) + item.type = 2 + item.draw = dropDownMenuItemDraw + item.eventHandler = dropDownMenuItemEventHandler + + menu:update() + + return item +end + +local function dropDownMenuScrollDown(workspace, menu) + if menu.itemsContainer.children[1].localY < 1 then + for i = 1, #menu.itemsContainer.children do + menu.itemsContainer.children[i].localY = menu.itemsContainer.children[i].localY + 1 + end + + dropDownMenuReposition(menu) + workspace:draw() + end +end + +local function dropDownMenuScrollUp(workspace, menu) + if menu.itemsContainer.children[#menu.itemsContainer.children].localY + menu.itemsContainer.children[#menu.itemsContainer.children].height - 1 > menu.height then + for i = 1, #menu.itemsContainer.children do + menu.itemsContainer.children[i].localY = menu.itemsContainer.children[i].localY - 1 + end + + dropDownMenuReposition(menu) + workspace:draw() + end +end + +local function dropDownMenuEventHandler(workspace, menu, e1, e2, e3, e4, e5) + if e1 == "scroll" then + if e5 == 1 then + dropDownMenuScrollDown(workspace, menu) + else + dropDownMenuScrollUp(workspace, menu) + end + end +end + +local function dropDownMenuPrevButtonOnTouch(workspace, button) + dropDownMenuScrollDown(workspace, button.parent) +end + +local function dropDownMenuNextButtonOnTouch(workspace, button) + dropDownMenuScrollUp(workspace, button.parent) +end + +local function dropDownMenuDraw(menu) + screen.drawRectangle(menu.x, menu.y, menu.width, menu.height, menu.colors.default.background, menu.colors.default.text, " ", menu.colors.transparency.background) + GUI.drawShadow(menu.x, menu.y, menu.width, menu.height, menu.colors.transparency.shadow, true) + containerDraw(menu) +end + +local function dropDownMenuBackgroundObjectEventHandler(workspace, object, e1) + if e1 == "touch" then + for i = 2, #object.parent.children do + if object.parent.children[i].onMenuClosed then + object.parent.children[i].onMenuClosed() + end + end + + object.parent:remove() + workspace:draw() + end +end + +local function dropDownMenuAdd(parentContainer, menu) + local container = parentContainer:addChild(GUI.container(1, 1, parentContainer.width, parentContainer.height)) + container:addChild(GUI.object(1, 1, container.width, container.height)).eventHandler = dropDownMenuBackgroundObjectEventHandler + + return container:addChild(menu:releaseItems()) +end + +function GUI.dropDownMenu(x, y, width, maximumHeight, itemHeight, backgroundColor, textColor, backgroundPressedColor, textPressedColor, disabledColor, separatorColor, backgroundTransparency, shadowTransparency) + local menu = GUI.container(x, y, width, 1) + + menu.colors = { + default = { + background = backgroundColor, + text = textColor + }, + selected = { + background = backgroundPressedColor, + text = textPressedColor + }, + disabled = { + text = disabledColor + }, + separator = separatorColor, + transparency = { + background = backgroundTransparency, + shadow = shadowTransparency + } + } + + menu.itemsContainer = menu:addChild(GUI.container(1, 1, menu.width, menu.height)) + menu.prevButton = menu:addChild(GUI.button(1, 1, menu.width, 1, backgroundColor, textColor, backgroundPressedColor, textPressedColor, "▲")) + menu.nextButton = menu:addChild(GUI.button(1, 1, menu.width, 1, backgroundColor, textColor, backgroundPressedColor, textPressedColor, "▼")) + menu.prevButton.colors.transparency, menu.nextButton.colors.transparency = backgroundTransparency, backgroundTransparency + menu.prevButton.onTouch = dropDownMenuPrevButtonOnTouch + menu.nextButton.onTouch = dropDownMenuNextButtonOnTouch + + menu.releaseItems = dropDownMenuReleaseItems + menu.itemHeight = itemHeight + menu.addSeparator = dropDownMenuAddSeparator + menu.addItem = dropDownMenuAddItem + menu.removeItem = dropDownMenuRemoveItem + menu.draw = dropDownMenuDraw + menu.maximumHeight = maximumHeight + menu.eventHandler = dropDownMenuEventHandler + menu.update = dropDownMenuUpdate + + return menu +end + +-------------------------------------------------------------------------------- + +local function contextMenuUpdate(menu) + if #menu.itemsContainer.children > 0 then + local widestItem, widestShortcut = 0, 0 + for i = 1, #menu.itemsContainer.children do + if menu.itemsContainer.children[i].type == 1 then + widestItem = math.max(widestItem, unicode.len(menu.itemsContainer.children[i].text)) + if menu.itemsContainer.children[i].shortcut then + widestShortcut = math.max(widestShortcut, unicode.len(menu.itemsContainer.children[i].shortcut)) + end + end + end + + menu.width, menu.height = 2 + widestItem + (widestShortcut > 0 and 3 + widestShortcut or 0), math.min(dropDownMenuGetHeight(menu), menu.maximumHeight) + dropDownMenuReposition(menu) + + local bufferWidth, bufferHeight = screen.getResolution() + if menu.x + menu.width + 1 >= bufferWidth then + menu.localX = bufferWidth - menu.width - 1 + end + if menu.y + menu.height >= bufferHeight then + menu.localY = bufferHeight - menu.height + end + end +end + +local contextMenuCreate, contextMenuAddSubMenu + +contextMenuAddSubMenu = function(menu, text, disabled) + local item = menu:addItem(text, disabled, "►") + item.subMenu = contextMenuCreate(1, 1) + item.subMenu.colors = menu.colors + + return item.subMenu +end + +contextMenuCreate = function(x, y, backgroundColor, textColor, backgroundPressedColor, textPressedColor, disabledColor, separatorColor, backgroundTransparency, shadowTransparency) + local menu = GUI.dropDownMenu( + x, + y, + 1, + math.ceil(screen.getHeight() * 0.5), + 1, + backgroundColor or GUI.CONTEXT_MENU_DEFAULT_BACKGROUND_COLOR, + textColor or GUI.CONTEXT_MENU_DEFAULT_TEXT_COLOR, + backgroundPressedColor or GUI.CONTEXT_MENU_PRESSED_BACKGROUND_COLOR, + textPressedColor or GUI.CONTEXT_MENU_PRESSED_TEXT_COLOR, + disabledColor or GUI.CONTEXT_MENU_DISABLED_COLOR, + separatorColor or GUI.CONTEXT_MENU_SEPARATOR_COLOR, + backgroundTransparency or GUI.CONTEXT_MENU_BACKGROUND_TRANSPARENCY, + shadowTransparency or GUI.CONTEXT_MENU_SHADOW_TRANSPARENCY + ) + + menu.update = contextMenuUpdate + menu.addSubMenu = contextMenuAddSubMenu + + return menu +end + +function GUI.addContextMenu(parentContainer, arg1, ...) + if type(arg1) == "table" then + return dropDownMenuAdd(parentContainer, arg1, ...) + else + return dropDownMenuAdd(parentContainer, contextMenuCreate(arg1, ...)) + end +end + +-------------------------------------------------------------------------------- + +local function comboBoxDraw(object) + screen.drawRectangle(object.x, object.y, object.width, object.height, object.colors.default.background, object.colors.default.text, " ") + if object.dropDownMenu.itemsContainer.children[object.selectedItem] then + screen.drawText(object.x + 1, math.floor(object.y + object.height / 2), object.colors.default.text, text.limit(object.dropDownMenu.itemsContainer.children[object.selectedItem].text, object.width - object.height - 2, "right")) + end + + local width = object.height * 2 - 1 + screen.drawRectangle(object.x + object.width - object.height * 2 + 1, object.y, width, object.height, object.colors.arrow.background, object.colors.arrow.text, " ") + screen.drawText(math.floor(object.x + object.width - width / 2), math.floor(object.y + object.height / 2), object.colors.arrow.text, object.pressed and "▲" or "▼") + + return object +end + +local function comboBoxGetItem(object, what) + if type(what) == "number" then + return object.dropDownMenu.itemsContainer.children[what] + else + local children = object.dropDownMenu.itemsContainer.children + for i = 1, #children do + if children[i].text == what then + return children[i], i + end + end + end +end + +local function comboBoxRemoveItem(object, index) + object.dropDownMenu:removeItem(index) + if object.selectedItem > #object.dropDownMenu.itemsContainer.children then + object.selectedItem = #object.dropDownMenu.itemsContainer.children + end +end + +local function comboBoxCount(object) + return #object.dropDownMenu.itemsContainer.children +end + +local function comboBoxClear(object) + object.dropDownMenu.itemsContainer:removeChildren() + object.selectedItem = 1 + + return object +end + +local function comboBoxEventHandler(workspace, object, e1, ...) + if e1 == "touch" and #object.dropDownMenu.itemsContainer.children > 0 then + object.pressed = true + object.dropDownMenu.x, object.dropDownMenu.y, object.dropDownMenu.width = object.x, object.y + object.height, object.width + object.dropDownMenu:update() + dropDownMenuAdd(workspace, object.dropDownMenu) + workspace:draw() + end +end + +local function comboBoxAddItem(object, ...) + return object.dropDownMenu:addItem(...) +end + +local function comboBoxAddSeparator(object) + return object.dropDownMenu:addSeparator() +end + +function GUI.comboBox(x, y, width, itemSize, backgroundColor, textColor, arrowBackgroundColor, arrowTextColor) + local comboBox = GUI.object(x, y, width, itemSize) + + comboBox.colors = { + default = { + background = backgroundColor, + text = textColor + }, + selected = { + background = GUI.CONTEXT_MENU_PRESSED_BACKGROUND_COLOR, + text = GUI.CONTEXT_MENU_PRESSED_TEXT_COLOR + }, + arrow = { + background = arrowBackgroundColor, + text = arrowTextColor + } + } + + comboBox.dropDownMenu = GUI.dropDownMenu( + 1, + 1, + 1, + math.ceil(screen.getHeight() * 0.5), + itemSize, + comboBox.colors.default.background, + comboBox.colors.default.text, + comboBox.colors.selected.background, + comboBox.colors.selected.text, + GUI.CONTEXT_MENU_DISABLED_COLOR, + GUI.CONTEXT_MENU_SEPARATOR_COLOR, + GUI.CONTEXT_MENU_BACKGROUND_TRANSPARENCY, + GUI.CONTEXT_MENU_SHADOW_TRANSPARENCY + ) + + comboBox.dropDownMenu.onMenuClosed = function(index) + comboBox.pressed = false + comboBox.selectedItem = index or comboBox.selectedItem + comboBox.firstParent:draw() + + if index and comboBox.onItemSelected then + comboBox.onItemSelected(index) + end + end + + comboBox.selectedItem = 1 + comboBox.addItem = comboBoxAddItem + comboBox.removeItem = comboBoxRemoveItem + comboBox.addSeparator = comboBoxAddSeparator + comboBox.draw = comboBoxDraw + comboBox.clear = comboBoxClear + comboBox.getItem = comboBoxGetItem + comboBox.count = comboBoxCount + comboBox.eventHandler = comboBoxEventHandler + + return comboBox +end + +--------------------------------------------------------------------------------------------------- + +local function windowDraw(window) + containerDraw(window) + GUI.drawShadow(window.x, window.y, window.width, window.height, GUI.WINDOW_SHADOW_TRANSPARENCY, true) + + return window +end + +local function windowCheck(window, x, y) + local child + for i = #window.children, 1, -1 do + child = window.children[i] + + if + not child.hidden and + not child.disabled and + child:isPointInside(x, y) + then + if not child.passScreenEvents and child.eventHandler then + return true + elseif child.children then + local result = windowCheck(child, x, y) + if result == true then + return true + elseif result == false then + return false + end + end + end + end +end + +local function windowEventHandler(workspace, window, e1, e2, e3, e4, ...) + if e1 == "touch" then + if not windowCheck(window, e3, e4) then + window.lastTouchX, window.lastTouchY = e3, e4 + end + + if window ~= window.parent.children[#window.parent.children] then + window:moveToFront() + + if window.onFocus then + window.onFocus(workspace, window, e1, e2, e3, e4, ...) + end + + workspace:draw() + end + elseif e1 == "drag" and window.lastTouchX and not windowCheck(window, e3, e4) then + local xOffset, yOffset = e3 - window.lastTouchX, e4 - window.lastTouchY + if xOffset ~= 0 or yOffset ~= 0 then + window.localX, window.localY = window.localX + xOffset, window.localY + yOffset + window.lastTouchX, window.lastTouchY = e3, e4 + + workspace:draw() + end + elseif e1 == "drop" then + window.lastTouchX, window.lastTouchY = nil, nil + end +end + +local function windowResize(window, width, height) + window.width, window.height = width, height + if window.onResize then + window.onResize(width, height) + end + + return window +end + +local function windowMaximize(window) + if window.maximized then + window.localX, window.localY = window.oldGeometryX, window.oldGeometryY + window:resize(window.oldGeometryWidth, window.oldGeometryHeight) + else + window.oldGeometryX, window.oldGeometryY, window.oldGeometryWidth, window.oldGeometryHeight = window.localX, window.localY, window.width, window.height + window.localX, window.localY = 1, 1 + window:resize(window.parent.width, window.parent.height) + end + + window.maximized = not window.maximized +end + +local function windowMinimize(window) + window.hidden = not window.hidden +end + +function GUI.window(x, y, width, height) + local window = GUI.container(x, y, width, height) + + window.passScreenEvents = false + + window.resize = windowResize + window.maximize = windowMaximize + window.minimize = windowMinimize + window.eventHandler = windowEventHandler + window.draw = windowDraw + + return window +end + +function GUI.filledWindow(x, y, width, height, backgroundColor) + local window = GUI.window(x, y, width, height) + + window.backgroundPanel = window:addChild(GUI.panel(1, 1, width, height, backgroundColor)) + window.actionButtons = window:addChild(GUI.actionButtons(2, 2, true)) + + return window +end + +function GUI.titledWindow(x, y, width, height, title, addTitlePanel) + local window = GUI.filledWindow(x, y, width, height, GUI.WINDOW_BACKGROUND_PANEL_COLOR) + + if addTitlePanel then + window.titlePanel = window:addChild(GUI.panel(1, 1, width, 1, GUI.WINDOW_TITLE_BACKGROUND_COLOR)) + window.backgroundPanel.localY, window.backgroundPanel.height = 2, window.height - 1 + end + + window.titleLabel = window:addChild(GUI.label(1, 1, width, height, GUI.WINDOW_TITLE_TEXT_COLOR, title)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + window.actionButtons.localY = 1 + window.actionButtons:moveToFront() + + return window +end + +function GUI.tabbedWindow(x, y, width, height, ...) + local window = GUI.filledWindow(x, y, width, height, GUI.WINDOW_BACKGROUND_PANEL_COLOR) + + window.tabBar = window:addChild(GUI.tabBar(1, 1, window.width, 3, 2, 0, GUI.WINDOW_TAB_BAR_DEFAULT_BACKGROUND_COLOR, GUI.WINDOW_TAB_BAR_DEFAULT_TEXT_COLOR, GUI.WINDOW_TAB_BAR_DEFAULT_BACKGROUND_COLOR, GUI.WINDOW_TAB_BAR_DEFAULT_TEXT_COLOR, GUI.WINDOW_TAB_BAR_SELECTED_BACKGROUND_COLOR, GUI.WINDOW_TAB_BAR_SELECTED_TEXT_COLOR, true)) + + window.backgroundPanel.localY, window.backgroundPanel.height = 4, window.height - 3 + window.actionButtons:moveToFront() + window.actionButtons.localY = 2 + + return window +end + +--------------------------------------------------------------------------------------------------- + +function GUI.tabBar(...) + local tabBar = GUI.list(...) + + tabBar:setDirection(GUI.DIRECTION_HORIZONTAL) + tabBar:setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + + return tabBar +end + +-------------------------------------------------------------------------------- + +local function menuDraw(menu) + screen.drawRectangle(menu.x, menu.y, menu.width, 1, menu.colors.default.background, menu.colors.default.text, " ", menu.colors.transparency) + layoutDraw(menu) +end + +local function menuAddItem(menu, text, textColor) + local item = menu:addChild(pressable(1, 1, unicode.len(text) + 2, 1, nil, textColor or menu.colors.default.text, menu.colors.selected.background, menu.colors.selected.text, 0x0, 0x0, text)) + item.eventHandler = pressableEventHandler + + return item +end + +local function menuGetItem(menu, what) + if type(what) == "number" then + return menu.children[what] + else + for i = 1, #menu.children do + if menu.children[i].text == what then + return menu.children[i], i + end + end + end +end + +local function menuContextMenuItemOnTouch(workspace, item) + item.contextMenu.x, item.contextMenu.y = item.x, item.y + 1 + dropDownMenuAdd(workspace, item.contextMenu) + + workspace:draw() +end + +local function menuAddContextMenu(menu, ...) + local item = menu:addItem(...) + + item.switchMode = true + item.onTouch = menuContextMenuItemOnTouch + item.contextMenu = contextMenuCreate(1, 1) + item.contextMenu.onMenuClosed = function() + item.pressed = false + item.firstParent:draw() + end + + return item.contextMenu +end + +function GUI.menu(x, y, width, backgroundColor, textColor, backgroundPressedColor, textPressedColor, backgroundTransparency) + local menu = GUI.layout(x, y, width, 1, 1, 1) + + menu.colors = { + default = { + background = backgroundColor, + text = textColor, + }, + selected = { + background = backgroundPressedColor, + text = textPressedColor, + }, + transparency = backgroundTransparency + } + + menu.passScreenEvents = false + menu.addContextMenu = menuAddContextMenu + menu.addItem = menuAddItem + menu.getItem = menuGetItem + menu.draw = menuDraw + + menu:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL) + menu:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP) + menu:setSpacing(1, 1, 0) + menu:setMargin(1, 1, 1, 0) + + return menu +end + +--------------------------------------------------------------------------------------------------- + +local function progressIndicatorDraw(self) + local color = self.active and (self.position == 1 and self.colors.secondary or self.colors.primary) or self.colors.passive + screen.drawText(self.x + 1, self.y, color, "⢀") + screen.drawText(self.x + 2, self.y, color, "⡀") + + color = self.active and (self.position == 2 and self.colors.secondary or self.colors.primary) or self.colors.passive + screen.drawText(self.x + 3, self.y + 1, color, "⠆") + screen.drawText(self.x + 2, self.y + 1, color, "⢈") + + color = self.active and (self.position == 3 and self.colors.secondary or self.colors.primary) or self.colors.passive + screen.drawText(self.x + 1, self.y + 2, color, "⠈") + screen.drawText(self.x + 2, self.y + 2, color, "⠁") + + color = self.active and (self.position == 4 and self.colors.secondary or self.colors.primary) or self.colors.passive + screen.drawText(self.x, self.y + 1, color, "⠰") + screen.drawText(self.x + 1, self.y + 1, color, "⡁") +end + +local function progressIndicatorRoll(self) + self.position = self.position + 1 + if self.position > 4 then + self.position = 1 + end +end + +local function progressIndicatorReset(self, state) + self.active = state + self.position = 1 +end + +function GUI.progressIndicator(x, y, passiveColor, primaryColor, secondaryColor) + local object = GUI.object(x, y, 4, 3) + + object.colors = { + passive = passiveColor, + primary = primaryColor, + secondary = secondaryColor + } + + object.active = false + object.reset = progressIndicatorReset + object.draw = progressIndicatorDraw + object.roll = progressIndicatorRoll + + object:reset() + + return object +end + +--------------------------------------------------------------------------------------------------- + +return GUI diff --git a/Libraries/Image.lua b/Libraries/Image.lua new file mode 100755 index 00000000..786a18b9 --- /dev/null +++ b/Libraries/Image.lua @@ -0,0 +1,443 @@ + +local color = require("Color") +local filesystem = require("Filesystem") + +-------------------------------------------------------------------------------- + +local image = {} + +local OCIFSignature = "OCIF" +local encodingMethodsLoad = {} +local encodingMethodsSave = {} + +-------------------------------------------------------------------------------- + +local function tableSize(t) + local size = 0 + for key in pairs(t) do + size = size + 1 + end + + return size +end + +local function group(picture, compressColors) + local groupedPicture, x, y, background, foreground = {}, 1, 1 + + for i = 3, #picture, 4 do + if compressColors then + background, foreground = color.to8Bit(picture[i]), color.to8Bit(picture[i + 1]) + if i % 603 == 0 then + computer.pullSignal(0) + end + else + background, foreground = picture[i], picture[i + 1] + end + + groupedPicture[picture[i + 2]] = groupedPicture[picture[i + 2]] or {} + groupedPicture[picture[i + 2]][picture[i + 3]] = groupedPicture[picture[i + 2]][picture[i + 3]] or {} + groupedPicture[picture[i + 2]][picture[i + 3]][background] = groupedPicture[picture[i + 2]][picture[i + 3]][background] or {} + groupedPicture[picture[i + 2]][picture[i + 3]][background][foreground] = groupedPicture[picture[i + 2]][picture[i + 3]][background][foreground] or {} + groupedPicture[picture[i + 2]][picture[i + 3]][background][foreground][y] = groupedPicture[picture[i + 2]][picture[i + 3]][background][foreground][y] or {} + + table.insert(groupedPicture[picture[i + 2]][picture[i + 3]][background][foreground][y], x) + + x = x + 1 + if x > picture[1] then + x, y = 1, y + 1 + end + end + + return groupedPicture +end + +encodingMethodsSave[5] = function(file, picture) + file:writeBytes( + bit32.rshift(picture[1], 8), + bit32.band(picture[2], 0xFF) + ) + + for i = 3, #picture, 4 do + file:writeBytes( + color.to8Bit(picture[i]), + color.to8Bit(picture[i + 1]), + math.floor(picture[i + 2] * 255) + ) + + file:write(picture[i + 3]) + end +end + +encodingMethodsLoad[5] = function(file, picture) + picture[1] = file:readBytes(2) + picture[2] = file:readBytes(2) + + for i = 1, image.getWidth(picture) * image.getHeight(picture) do + table.insert(picture, color.to24Bit(file:readBytes(1))) + table.insert(picture, color.to24Bit(file:readBytes(1))) + table.insert(picture, file:readBytes(1) / 255) + table.insert(picture, file:readUnicodeChar()) + end +end + +encodingMethodsSave[6] = function(file, picture) + -- Grouping picture by it's alphas, symbols and colors + local groupedPicture = group(picture, true) + + -- Writing 1 byte per image width and height + file:writeBytes( + picture[1], + picture[2] + ) + + -- Writing 1 byte for alphas array size + file:writeBytes(tableSize(groupedPicture)) + + for alpha in pairs(groupedPicture) do + local symbolsSize = tableSize(groupedPicture[alpha]) + + file:writeBytes( + -- Writing 1 byte for current alpha value + math.floor(alpha * 255), + -- Writing 2 bytes for symbols array size + bit32.rshift(symbolsSize, 8), + bit32.band(symbolsSize, 0xFF) + ) + + for symbol in pairs(groupedPicture[alpha]) do + -- Writing current unicode symbol value + file:write(symbol) + -- Writing 1 byte for backgrounds array size + file:writeBytes(tableSize(groupedPicture[alpha][symbol])) + + for background in pairs(groupedPicture[alpha][symbol]) do + file:writeBytes( + -- Writing 1 byte for background color value (compressed by color) + background, + -- Writing 1 byte for foregrounds array size + tableSize(groupedPicture[alpha][symbol][background]) + ) + + for foreground in pairs(groupedPicture[alpha][symbol][background]) do + file:writeBytes( + -- Writing 1 byte for foreground color value (compressed by color) + foreground, + -- Writing 1 byte for y array size + tableSize(groupedPicture[alpha][symbol][background][foreground]) + ) + + for y in pairs(groupedPicture[alpha][symbol][background][foreground]) do + file:writeBytes( + -- Writing 1 byte for current y value + y, + -- Writing 1 byte for x array size + #groupedPicture[alpha][symbol][background][foreground][y] + ) + + for x = 1, #groupedPicture[alpha][symbol][background][foreground][y] do + file:writeBytes(groupedPicture[alpha][symbol][background][foreground][y][x]) + end + end + end + end + end + end +end + +encodingMethodsLoad[6] = function(file, picture) + picture[1] = file:readBytes(1) + picture[2] = file:readBytes(1) + + local currentAlpha, currentSymbol, currentBackground, currentForeground, currentY + local alphaSize, symbolSize, backgroundSize, foregroundSize, ySize, xSize + + alphaSize = file:readBytes(1) + + for alpha = 1, alphaSize do + currentAlpha = file:readBytes(1) / 255 + symbolSize = file:readBytes(2) + + for symbol = 1, symbolSize do + currentSymbol = file:readUnicodeChar() + backgroundSize = file:readBytes(1) + + for background = 1, backgroundSize do + currentBackground = color.to24Bit(file:readBytes(1)) + foregroundSize = file:readBytes(1) + + for foreground = 1, foregroundSize do + currentForeground = color.to24Bit(file:readBytes(1)) + ySize = file:readBytes(1) + + for y = 1, ySize do + currentY = file:readBytes(1) + xSize = file:readBytes(1) + + for x = 1, xSize do + image.set( + picture, + file:readBytes(1), + currentY, + currentBackground, + currentForeground, + currentAlpha, + currentSymbol + ) + end + end + end + end + end + end +end + +-------------------------------------------------------------------------------- + +function image.getIndex(x, y, width) + return 4 * (width * (y - 1) + x) - 1 +end + +function image.create(width, height, background, foreground, alpha, symbol, random) + local picture = {width, height} + + for i = 1, width * height do + table.insert(picture, random and math.random(0x0, 0xFFFFFF) or (background or 0x0)) + table.insert(picture, random and math.random(0x0, 0xFFFFFF) or (foreground or 0x0)) + table.insert(picture, alpha or 0x0) + table.insert(picture, random and string.char(math.random(65, 90)) or (symbol or " ")) + end + + return picture +end + +function image.copy(picture) + local newPicture = {} + + for i = 1, #picture do + newPicture[i] = picture[i] + end + + return newPicture +end + +function image.save(path, picture, encodingMethod) + encodingMethod = encodingMethod or 6 + + local file, reason = filesystem.open(path, "wb") + if file then + if encodingMethodsSave[encodingMethod] then + file:write(OCIFSignature, string.char(encodingMethod)) + + local result, reason = xpcall(encodingMethodsSave[encodingMethod], debug.traceback, file, picture) + + file:close() + + if result then + return true + else + return false, "Failed to save OCIF image: " .. tostring(reason) + end + else + file:close() + return false, "Failed to save OCIF image: encoding method \"" .. tostring(encodingMethod) .. "\" is not supported" + end + else + return false, "Failed to open file for writing: " .. tostring(reason) + end +end + +function image.load(path) + local file, reason = filesystem.open(path, "rb") + if file then + local readedSignature = file:readString(#OCIFSignature) + if readedSignature == OCIFSignature then + local encodingMethod = file:readBytes(1) + if encodingMethodsLoad[encodingMethod] then + local picture = {} + local result, reason = xpcall(encodingMethodsLoad[encodingMethod], debug.traceback, file, picture) + + file:close() + + if result then + return picture + else + return false, "Failed to load OCIF image: " .. tostring(reason) + end + else + file:close() + return false, "Failed to load OCIF image: encoding method \"" .. tostring(encodingMethod) .. "\" is not supported" + end + else + file:close() + return false, "Failed to load OCIF image: binary signature \"" .. tostring(readedSignature) .. "\" is not valid" + end + else + return false, "Failed to open file \"" .. tostring(path) .. "\" for reading: " .. tostring(reason) + end +end + +------------------------------------------------------------------------------- + +function image.toString(picture) + local charArray = { + string.format("%02X", picture[1]), + string.format("%02X", picture[2]) + } + + for i = 3, #picture, 4 do + table.insert(charArray, string.format("%02X", color.to8Bit(picture[i]))) + table.insert(charArray, string.format("%02X", color.to8Bit(picture[i + 1]))) + table.insert(charArray, string.format("%02X", math.floor(picture[i + 2] * 255))) + table.insert(charArray, picture[i + 3]) + + if i % 603 == 0 then + computer.pullSignal(0) + end + end + + return table.concat(charArray) +end + +function image.fromString(pictureString) + local picture = { + tonumber("0x" .. unicode.sub(pictureString, 1, 2)), + tonumber("0x" .. unicode.sub(pictureString, 3, 4)), + } + + for i = 5, unicode.len(pictureString), 7 do + table.insert(picture, color.to24Bit(tonumber("0x" .. unicode.sub(pictureString, i, i + 1)))) + table.insert(picture, color.to24Bit(tonumber("0x" .. unicode.sub(pictureString, i + 2, i + 3)))) + table.insert(picture, tonumber("0x" .. unicode.sub(pictureString, i + 4, i + 5)) / 255) + table.insert(picture, unicode.sub(pictureString, i + 6, i + 6)) + end + + return picture +end + +-------------------------------------------------------------------------------- + +function image.set(picture, x, y, background, foreground, alpha, symbol) + local index = image.getIndex(x, y, picture[1]) + picture[index], picture[index + 1], picture[index + 2], picture[index + 3] = background, foreground, alpha, symbol + + return picture +end + +function image.get(picture, x, y) + local index = image.getIndex(x, y, picture[1]) + return picture[index], picture[index + 1], picture[index + 2], picture[index + 3] +end + +function image.getSize(picture) + return picture[1], picture[2] +end + +function image.getWidth(picture) + return picture[1] +end + +function image.getHeight(picture) + return picture[2] +end + +function image.transform(picture, newWidth, newHeight) + local newPicture, stepWidth, stepHeight, background, foreground, alpha, symbol = {newWidth, newHeight}, picture[1] / newWidth, picture[2] / newHeight + + local x, y = 1, 1 + for j = 1, newHeight do + for i = 1, newWidth do + background, foreground, alpha, symbol = image.get(picture, math.floor(x), math.floor(y)) + table.insert(newPicture, background) + table.insert(newPicture, foreground) + table.insert(newPicture, alpha) + table.insert(newPicture, symbol) + + x = x + stepWidth + end + + x, y = 1, y + stepHeight + end + + return newPicture +end + +function image.crop(picture, fromX, fromY, width, height) + if fromX >= 1 and fromY >= 1 and fromX + width - 1 <= picture[1] and fromY + height - 1 <= picture[2] then + local newPicture, background, foreground, alpha, symbol = {width, height} + + for y = fromY, fromY + height - 1 do + for x = fromX, fromX + width - 1 do + background, foreground, alpha, symbol = image.get(picture, x, y) + table.insert(newPicture, background) + table.insert(newPicture, foreground) + table.insert(newPicture, alpha) + table.insert(newPicture, symbol) + end + end + + return newPicture + else + return false, "Failed to crop image: target coordinates are out of source range" + end +end + +function image.flipHorizontally(picture) + local newPicture, background, foreground, alpha, symbol = {picture[1], picture[2]} + + for y = 1, picture[2] do + for x = picture[1], 1, -1 do + background, foreground, alpha, symbol = image.get(picture, x, y) + table.insert(newPicture, background) + table.insert(newPicture, foreground) + table.insert(newPicture, alpha) + table.insert(newPicture, symbol) + end + end + + return newPicture +end + +function image.flipVertically(picture) + local newPicture, background, foreground, alpha, symbol = {picture[1], picture[2]} + + for y = picture[2], 1, -1 do + for x = 1, picture[1] do + background, foreground, alpha, symbol = image.get(picture, x, y) + table.insert(newPicture, background) + table.insert(newPicture, foreground) + table.insert(newPicture, alpha) + table.insert(newPicture, symbol) + end + end + + return newPicture +end + +function image.expand(picture, fromTop, fromBottom, fromLeft, fromRight, background, foreground, alpha, symbol) + local newPicture = image.create(picture[1] + fromRight + fromLeft, picture[2] + fromTop + fromBottom, background, foreground, alpha, symbol) + + for y = 1, picture[2] do + for x = 1, picture[1] do + image.set(newPicture, x + fromLeft, y + fromTop, image.get(picture, x, y)) + end + end + + return newPicture +end + +function image.blend(picture, blendColor, transparency) + local newPicture = {picture[1], picture[2]} + + for i = 3, #picture, 4 do + table.insert(newPicture, color.blend(picture[i], blendColor, transparency)) + table.insert(newPicture, color.blend(picture[i + 1], blendColor, transparency)) + table.insert(newPicture, picture[i + 2]) + table.insert(newPicture, picture[i + 3]) + end + + return newPicture +end + +-------------------------------------------------------------------------------- + +return image diff --git a/lib/web.lua b/Libraries/Internet.lua similarity index 76% rename from lib/web.lua rename to Libraries/Internet.lua index 380241ca..092245fa 100755 --- a/lib/web.lua +++ b/Libraries/Internet.lua @@ -1,11 +1,10 @@ -local component = require("component") -local fs = require("filesystem") +local filesystem = require("Filesystem") ---------------------------------------------------------------------------------------------------- local function encode(data) - data = data:gsub("([^%-%_%.%~])", function(char) + data = data:gsub("([^%w%-%_%.%~])", function(char) return string.format("%%%02X", string.byte(char)) end) @@ -47,7 +46,7 @@ end ---------------------------------------------------------------------------------------------------- local function rawRequest(url, postData, headers, chunkHandler, chunkSize) - local pcallSuccess, requestHandle, requestReason = pcall(component.internet.request, url, postData, headers) + local pcallSuccess, requestHandle, requestReason = pcall(component.get("internet").request, url, postData, headers) if pcallSuccess then if requestHandle then while true do @@ -67,7 +66,7 @@ local function rawRequest(url, postData, headers, chunkHandler, chunkSize) return false, "Invalid URL-address" end else - return false, "Invalid arguments to component.internet.request" + return false, "Invalid arguments to internet.request" end end @@ -85,9 +84,9 @@ local function request(url, postData, headers) end local function download(url, path) - fs.makeDirectory(fs.path(path) or "") + filesystem.makeDirectory(filesystem.path(path) or "") - local handle, reason = io.open(path, "w") + local handle, reason = filesystem.open(path, "w") if handle then local success, reason = rawRequest(url, nil, nil, function(chunk) handle:write(chunk) @@ -97,7 +96,7 @@ local function download(url, path) if success then return true else - fs.remove(path) + filesystem.remove(path) return false, reason end else @@ -108,16 +107,16 @@ end local function run(url, ...) local result, reason = request(url) if result then - result, reason = load(result) + result, reason = load(result, "=script") if result then - result = {pcall(result, ...)} + result = {xpcall(result, debug.traceback, ...)} if result[1] then return table.unpack(result, 2) else - return false, "Failed to run script: " .. tostring(result[2]) + return false, tostring(result[2]) end else - return false, "Failed to run script: " .. tostring(loadReason) + return false, tostring(loadReason) end else return false, reason @@ -126,19 +125,6 @@ end ---------------------------------------------------------------------------------------------------- --- print(serialize({ --- array = { --- pidor = "English Test 123-_.~", --- tyan = 512, --- second = { --- zalupa = 421, --- penis = "Член" --- } --- }, --- }, true)) - ----------------------------------------------------------------------------------------------------- - return { encode = encode, serialize = serialize, diff --git a/lib/json.lua b/Libraries/JSON.lua old mode 100644 new mode 100755 similarity index 100% rename from lib/json.lua rename to Libraries/JSON.lua diff --git a/Libraries/Keyboard.lua b/Libraries/Keyboard.lua new file mode 100755 index 00000000..59f71eec --- /dev/null +++ b/Libraries/Keyboard.lua @@ -0,0 +1,41 @@ + +local keyboard = {} +local pressedCodes = {} + +------------------------------------------------------------------------------- + +function keyboard.isKeyDown(code) + checkArg(1, code, "number") + + return pressedCodes[code] +end + +function keyboard.isControl(code) + return type(code) == "number" and (code < 32 or (code >= 127 and code <= 159)) +end + +function keyboard.isAltDown(address) + return pressedCodes[56] or pressedCodes[184] +end + +function keyboard.isControlDown(address) + return pressedCodes[29] or pressedCodes[157] +end + +function keyboard.isShiftDown(address) + return pressedCodes[42] or pressedCodes[54] +end + +------------------------------------------------------------------------------- + +require("Event").addHandler(function(e1, _, _, e4) + if e1 == "key_down" then + pressedCodes[e4] = true + elseif e1 == "key_up" then + pressedCodes[e4] = nil + end +end) + +------------------------------------------------------------------------------- + +return keyboard diff --git a/lib/MeowEngine/Main.lua b/Libraries/MeowEngine/Main.lua similarity index 98% rename from lib/MeowEngine/Main.lua rename to Libraries/MeowEngine/Main.lua index dacf0eba..1a713da6 100755 --- a/lib/MeowEngine/Main.lua +++ b/Libraries/MeowEngine/Main.lua @@ -1,8 +1,9 @@ -------------------------------------------------------- Libraries -------------------------------------------------------- -local buffer = require("doubleBuffering") -local vector = require("vector") +local event = require("Event") +local screen = require("Screen") +local vector = require("Vector") local OCGL = require("OpenComputersGL/Main") local renderer = require("OpenComputersGL/Renderer") local materials = require("OpenComputersGL/Materials") @@ -296,7 +297,7 @@ local function sceneAddObjects(scene, objects) end local function sceneRender(scene) - renderer.setViewport( 1, 1, buffer.getWidth(), buffer.getHeight() * 2, scene.camera.nearClippingSurface, scene.camera.farClippingSurface, scene.camera.projectionSurface) + renderer.setViewport( 1, 1, screen.getWidth(), screen.getHeight() * 2, scene.camera.nearClippingSurface, scene.camera.farClippingSurface, scene.camera.projectionSurface) OCGL.clearBuffer(scene.backgroundColor) OCGL.renderMode = scene.renderMode OCGL.auxiliaryMode = scene.auxiliaryMode @@ -497,20 +498,20 @@ function meowEngine.intro(vector3Position, size) scene.camera:translate(speed, 0, 0) scene.camera:lookAt(0, 0, 0) scene:render() - if scene.camera.position[1] < to then buffer.clear(0x0, transparency) end - buffer.drawChanges() + if scene.camera.position[1] < to then screen.clear(0x0, transparency) end + screen.update() transparency = transparency + transparencyStep -- ecs.error("POS: " .. scene.camera.position[1] .. ", " .. scene.camera.position[2] .. ", " .. scene.camera.position[3] .. ", ROT: " .. math.deg(scene.camera.rotation[1]) .. ", " .. math.deg(scene.camera.rotation[2]) .. ", " .. math.deg(scene.camera.rotation[3])) - os.sleep(0.01) + event.sleep(0.01) end - os.sleep(2) + event.sleep(2) for i = 1, 0, -0.2 do scene:render() - buffer.clear(0x0, i) - buffer.drawChanges() + screen.clear(0x0, i) + screen.update() end end diff --git a/lib/MineOSNetwork.lua b/Libraries/Network.lua similarity index 57% rename from lib/MineOSNetwork.lua rename to Libraries/Network.lua index e2cde789..c0b837c9 100755 --- a/lib/MineOSNetwork.lua +++ b/Libraries/Network.lua @@ -1,72 +1,66 @@ -local component = require("component") -local computer = require("computer") -local MineOSCore = require("MineOSCore") -local MineOSPaths = require("MineOSPaths") local GUI = require("GUI") -local event = require("event") -local fs = require("filesystem") -local MineOSNetwork = {} +local event = require("Event") +local filesystem = require("Filesystem") +local system = require("System") +local paths = require("Paths") +local text = require("Text") + +local network = {} ---------------------------------------------------------------------------------------------------------------- -local filesystemProxy = component.proxy(computer.getBootAddress()) +local filesystemProxy = filesystem.getProxy() -MineOSNetwork.filesystemHandles = {} +network.filesystemHandles = {} -local modemMaxPacketSize = 8192 -local modemPacketReserve = 128 -MineOSNetwork.modemProxy = nil -MineOSNetwork.modemPort = 1488 -MineOSNetwork.modemTimeout = 2 +network.modemProxy = nil +network.modemPort = 1488 +network.modemPacketReserve = 128 +network.modemTimeout = 2 -MineOSNetwork.internetProxy = nil -MineOSNetwork.internetDelay = 0.05 -MineOSNetwork.internetTimeout = 0.25 +network.internetProxy = nil +network.internetDelay = 0.05 +network.internetTimeout = 0.25 -MineOSNetwork.proxySpaceUsed = 0 -MineOSNetwork.proxySpaceTotal = 1073741824 - -MineOSNetwork.mountPaths = { - modem = MineOSPaths.network .. "Modem/", - FTP = MineOSPaths.network .. "FTP/" -} +network.proxySpaceUsed = 0 +network.proxySpaceTotal = 1073741824 ---------------------------------------------------------------------------------------------------------------- -local function umountProxy(type) - for proxy in fs.mounts() do +local function unmountProxy(type) + for proxy in filesystem.mounts() do if proxy[type] then - fs.umount(proxy) + filesystem.unmount(proxy) end end end -function MineOSNetwork.updateComponents() - local modemAddress, internetAddress = component.list("modem")(), component.list("internet")() - if modemAddress then - MineOSNetwork.modemProxy = component.proxy(modemAddress) - MineOSNetwork.modemProxy.open(MineOSNetwork.modemPort) +function network.updateComponents() + local modem, internet = component.get("modem"), component.get("internet") + if modem then + network.modemProxy = modem + network.modemProxy.open(network.modemPort) else - MineOSNetwork.modemProxy = nil - MineOSNetwork.umountModems() + network.modemProxy = nil + network.unmountModems() end - if internetAddress then - MineOSNetwork.internetProxy = component.proxy(internetAddress) + if internet then + network.internetProxy = internet else - MineOSNetwork.internetProxy = nil - MineOSNetwork.umountFTPs() + network.internetProxy = nil + network.unmountFTPs() end end ---------------------------------------------------------------------------------------------------------------- -function MineOSNetwork.umountFTPs() - umountProxy("MineOSNetworkFTP") +function network.unmountFTPs() + unmountProxy("networkFTP") end -function MineOSNetwork.getFTPProxyName(address, port, user) +function network.getFTPProxyName(address, port, user) return user .. "@" .. address .. ":" .. port end @@ -80,9 +74,9 @@ local function FTPSocketWrite(socketHandle, data) end local function FTPSocketRead(socketHandle) - os.sleep(MineOSNetwork.internetDelay) + event.sleep(network.internetDelay) - local deadline, data, success, result = computer.uptime() + MineOSNetwork.internetTimeout, "" + local deadline, data, success, result = computer.uptime() + network.internetTimeout, "" while computer.uptime() < deadline do success, result = pcall(socketHandle.read, math.huge) if success then @@ -91,7 +85,7 @@ local function FTPSocketRead(socketHandle) return true, data end else - data, deadline = data .. result, computer.uptime() + MineOSNetwork.internetTimeout + data, deadline = data .. result, computer.uptime() + network.internetTimeout end else return false, result @@ -139,9 +133,9 @@ local function FTPEnterPassiveModeAndRunCommand(commandSocketHandle, command, da FTPSocketWrite(commandSocketHandle, command) - local dataSocketHandle = MineOSNetwork.internetProxy.connect(address, port) + local dataSocketHandle = network.internetProxy.connect(address, port) if dataToWrite then - os.sleep(MineOSNetwork.internetDelay) + event.sleep(network.internetDelay) dataSocketHandle.read(1) dataSocketHandle.write(dataToWrite) dataSocketHandle.close() @@ -216,32 +210,32 @@ local function check(...) return table.unpack(result) end -function MineOSNetwork.connectToFTP(address, port, user, password) - if MineOSNetwork.internetProxy then - local socketHandle, reason = MineOSNetwork.internetProxy.connect(address, port) +function network.connectToFTP(address, port, user, password) + if network.internetProxy then + local socketHandle, reason = network.internetProxy.connect(address, port) if socketHandle then FTPSocketRead(socketHandle) local result, reason = FTPLogin(socketHandle, user, password) if result then - local proxy, fileHandles, label = {}, {}, MineOSNetwork.getFTPProxyName(address, port, user) + local proxy, fileHandles, label = {}, {}, network.getFTPProxyName(address, port, user) proxy.type = "filesystem" proxy.slot = 0 proxy.address = label - proxy.MineOSNetworkFTP = true + proxy.networkFTP = true proxy.getLabel = function() return label end proxy.spaceUsed = function() - return MineOSNetwork.proxySpaceUsed + return network.proxySpaceUsed end proxy.spaceTotal = function() - return MineOSNetwork.proxySpaceTotal + return network.proxySpaceTotal end proxy.setLabel = function(text) @@ -254,7 +248,7 @@ function MineOSNetwork.connectToFTP(address, port, user, password) end proxy.closeSocketHandle = function() - fs.umount(proxy) + filesystem.unmount(proxy) return socketHandle.close() end @@ -312,13 +306,12 @@ function MineOSNetwork.connectToFTP(address, port, user, password) end proxy.open = function(path, mode) - local temporaryPath = MineOSCore.getTemporaryPath() + local temporaryPath = system.getTemporaryPath() if mode == "r" or mode == "rb" or mode == "a" or mode == "ab" then local success, result = FTPEnterPassiveModeAndRunCommand(socketHandle, "RETR " .. path) - local fileHandle = io.open(temporaryPath, "wb") - fileHandle:write(success and result or "") - fileHandle:close() + + filesystem.write(temporaryPath, success and result or "") end local fileHandle, reason = filesystemProxy.open(temporaryPath, mode) @@ -337,14 +330,12 @@ function MineOSNetwork.connectToFTP(address, port, user, password) filesystemProxy.close(fileHandle) if fileHandles[fileHandle].needUpload then - local file = io.open(fileHandles[fileHandle].temporaryPath, "rb") - local data = file:read("*a") - file:close() + local data = filesystem.read(fileHandles[fileHandle].temporaryPath) check(FTPEnterPassiveModeAndRunCommand(socketHandle, "STOR " .. fileHandles[fileHandle].path, data)) end - fs.remove(fileHandles[fileHandle].temporaryPath) + filesystem.remove(fileHandles[fileHandle].temporaryPath) fileHandles[fileHandle] = nil end @@ -399,80 +390,81 @@ end ---------------------------------------------------------------------------------------------------------------- -function MineOSNetwork.umountModems() - umountProxy("MineOSNetworkModem") +function network.unmountModems() + unmountProxy("networkModem") end -function MineOSNetwork.getModemProxyName(proxy) +function network.getModemProxyName(proxy) return proxy.name and proxy.name .. " (" .. proxy.address .. ")" or proxy.address end -function MineOSNetwork.getMountedModemProxy(address) - for proxy, path in fs.mounts() do - if proxy.MineOSNetworkModem and proxy.address == address then +function network.getMountedModemProxy(address) + for proxy, path in filesystem.mounts() do + if proxy.networkModem and proxy.address == address then return proxy end end end -function MineOSNetwork.sendMessage(address, ...) - if MineOSNetwork.modemProxy then - return MineOSNetwork.modemProxy.send(address, MineOSNetwork.modemPort, ...) +function network.sendMessage(address, ...) + if network.modemProxy then + return network.modemProxy.send(address, network.modemPort, ...) else - MineOSNetwork.modemProxy = nil + network.modemProxy = nil return false, "Modem component is not available" end end -function MineOSNetwork.broadcastMessage(...) - if MineOSNetwork.modemProxy then - return MineOSNetwork.modemProxy.broadcast(MineOSNetwork.modemPort, ...) +function network.broadcastMessage(...) + if network.modemProxy then + return network.modemProxy.broadcast(network.modemPort, ...) else - MineOSNetwork.modemProxy = nil + network.modemProxy = nil return false, "Modem component is not available" end end -function MineOSNetwork.setSignalStrength(strength) - if MineOSNetwork.modemProxy then - if MineOSNetwork.modemProxy.isWireless() then - return MineOSNetwork.modemProxy.setStrength(strength) +function network.setSignalStrength(strength) + if network.modemProxy then + if network.modemProxy.isWireless() then + return network.modemProxy.setStrength(strength) else return false, "Modem component is not wireless" end else - MineOSNetwork.modemProxy = nil + network.modemProxy = nil return false, "Modem component is not available" end end -function MineOSNetwork.broadcastComputerState(state) - return MineOSNetwork.broadcastMessage("MineOSNetwork", state and "computerAvailable" or "computerNotAvailable", MineOSCore.properties.network.name) +function network.broadcastComputerState(state) + return network.broadcastMessage("network", state and "computerAvailable" or "computerNotAvailable", system.properties.networkName) end local function newModemProxy(address) local function request(method, returnOnFailure, ...) - MineOSNetwork.sendMessage(address, "MineOSNetwork", "request", method, ...) + network.sendMessage(address, "network", "request", method, ...) while true do - local eventData = { event.pull(MineOSNetwork.modemTimeout, "modem_message") } - - if eventData[3] == address and eventData[6] == "MineOSNetwork" then - if eventData[7] == "response" and eventData[8] == method then - return table.unpack(eventData, 9) - elseif eventData[7] == "accessDenied" then - computer.pushSignal("MineOSNetwork", "accessDenied", address) - return returnOnFailure, "Access denied" - end - elseif not eventData[1] then - local proxy = MineOSNetwork.getMountedModemProxy(address) - if proxy then - fs.umount(proxy) - end + local eventData = { event.pull(network.modemTimeout) } + if eventData[1] == "modem_message" then + if eventData[3] == address and eventData[6] == "network" then + if eventData[7] == "response" and eventData[8] == method then + return table.unpack(eventData, 9) + elseif eventData[7] == "accessDenied" then + computer.pushSignal("network", "accessDenied", address) + return returnOnFailure, "Access denied" + end + elseif not eventData[1] then + local proxy = network.getMountedModemProxy(address) + if proxy then + filesystem.unmount(proxy) + end - computer.pushSignal("MineOSNetwork", "timeout") + computer.pushSignal("network", "timeout") - return returnOnFailure, "Network filesystem timeout" + return returnOnFailure, "Network filesystem timeout" + end end end end @@ -482,7 +474,7 @@ local function newModemProxy(address) proxy.type = "filesystem" proxy.address = address proxy.slot = 0 - proxy.MineOSNetworkModem = true + proxy.networkModem = true proxy.getLabel = function() return request("getLabel", "N/A") @@ -493,11 +485,11 @@ local function newModemProxy(address) end proxy.spaceUsed = function() - return request("spaceUsed", MineOSNetwork.proxySpaceUsed) + return request("spaceUsed", network.proxySpaceUsed) end proxy.spaceTotal = function() - return request("spaceTotal", MineOSNetwork.proxySpaceTotal) + return request("spaceTotal", network.proxySpaceTotal) end proxy.exists = function(path) @@ -529,7 +521,7 @@ local function newModemProxy(address) end proxy.list = function(path) - return table.fromString(request("list", "{}", path)) + return text.deserialize(request("list", "{}", path)) end proxy.open = function(path, mode) @@ -548,8 +540,8 @@ local function newModemProxy(address) return request("read", "", ...) end - proxy.write = function(handle, data) - local maxPacketSize = (MineOSNetwork.modemProxy.maxPacketSize and MineOSNetwork.modemProxy.maxPacketSize() or modemMaxPacketSize) - modemPacketReserve + write = function(handle, data) + local maxPacketSize = network.modemProxy.maxPacketSize() - network.modemPacketReserve repeat if not request("write", false, handle, data:sub(1, maxPacketSize)) then return false @@ -561,10 +553,10 @@ local function newModemProxy(address) end proxy.rename = function(from, to) - local proxyFrom = fs.get(from) - local proxyTo = fs.get(to) + local proxyFrom = filesystem.get(from) + local proxyTo = filesystem.get(to) - if proxyFrom.MineOSNetworkModem or proxyTo.MineOSNetworkModem then + if proxyFrom.networkModem or proxyTo.networkModem then local success, handleFrom, handleTo, data, reason = true handleFrom, reason = proxyFrom.open(from, "rb") @@ -609,133 +601,137 @@ end local exceptionMethods = { getLabel = function() - return MineOSCore.properties.network.name or MineOSNetwork.modemProxy.address + return system.properties.networkName or network.modemProxy.address end, list = function(path) - return table.toString(filesystemProxy.list(path)) + return text.serialize(filesystemProxy.list(path)) end, open = function(path, mode) local ID while not ID do ID = math.random(1, 0x7FFFFFFF) - for handleID in pairs(MineOSNetwork.filesystemHandles) do + for handleID in pairs(network.filesystemHandles) do if handleID == ID then ID = nil end end end - MineOSNetwork.filesystemHandles[ID] = filesystemProxy.open(path, mode) + network.filesystemHandles[ID] = filesystemProxy.open(path, mode) return ID end, close = function(ID) - local data, reason = filesystemProxy.close(MineOSNetwork.filesystemHandles[ID]) - MineOSNetwork.filesystemHandles[ID] = nil + local data, reason = filesystemProxy.close(network.filesystemHandles[ID]) + network.filesystemHandles[ID] = nil return data, reason end, read = function(ID, ...) - return filesystemProxy.read(MineOSNetwork.filesystemHandles[ID], ...) + return filesystemProxy.read(network.filesystemHandles[ID], ...) end, write = function(ID, ...) - return filesystemProxy.write(MineOSNetwork.filesystemHandles[ID], ...) + return filesystemProxy.write(network.filesystemHandles[ID], ...) end, seek = function(ID, ...) - return filesystemProxy.seek(MineOSNetwork.filesystemHandles[ID], ...) + return filesystemProxy.seek(network.filesystemHandles[ID], ...) end, } -local function handleRequest(e1, e2, e3, e4, e5, e6, e7, e8, ...) - if MineOSCore.properties.network.users[e3].allowReadAndWrite then - local result = { pcall(exceptionMethods[e8] or filesystemProxy[e8], ...) } - MineOSNetwork.sendMessage(e3, "MineOSNetwork", "response", e8, table.unpack(result, result[1] and 2 or 1)) +local function handleRequest(eventData) + if system.properties.networkUsers[eventData[3]].allowReadAndWrite then + local result = { pcall(exceptionMethods[eventData[8]] or filesystemProxy[eventData[8]], table.unpack(eventData, 9)) } + network.sendMessage(eventData[3], "network", "response", eventData[8], table.unpack(result, result[1] and 2 or 1)) else - MineOSNetwork.sendMessage(e3, "MineOSNetwork", "accessDenied") + network.sendMessage(eventData[3], "network", "accessDenied") end end ---------------------------------------------------------------------------------------------------------------- -function MineOSNetwork.update() - MineOSNetwork.umountModems() - MineOSNetwork.umountFTPs() - MineOSNetwork.updateComponents() - MineOSNetwork.setSignalStrength(MineOSCore.properties.network.signalStrength) - MineOSNetwork.broadcastComputerState(MineOSCore.properties.network.enabled) +function network.update() + network.unmountModems() + network.unmountFTPs() + network.updateComponents() + network.setSignalStrength(system.properties.networkSignalStrength) + network.broadcastComputerState(system.properties.networkEnabled) - -- if MineOSNetwork.eventHandlerID then - -- event.removeHandler(MineOSNetwork.eventHandlerID) - -- end + if network.eventHandlerID then + event.removeHandler(network.eventHandlerID) + end - -- if MineOSCore.properties.network.enabled then - - -- end -end - -function MineOSNetwork.disable() - MineOSCore.properties.network.enabled = false - MineOSCore.saveProperties() - MineOSNetwork.update() -end - -function MineOSNetwork.enable() - MineOSCore.properties.network.enabled = true - MineOSCore.saveProperties() - MineOSNetwork.update() -end - ----------------------------------------------------------------------------------------------------------------- - -event.register( - nil, - function(e1, e2, e3, e4, e5, e6, e7, e8, ...) - if (e1 == "component_added" or e1 == "component_removed") and (e3 == "modem" or e3 == "internet") then - MineOSNetwork.updateComponents() - MineOSNetwork.broadcastComputerState(MineOSCore.properties.network.enabled) - elseif MineOSCore.properties.network.enabled and e1 == "modem_message" and e6 == "MineOSNetwork" then - if e7 == "request" then - handleRequest(e1, e2, e3, e4, e5, e6, e7, e8, ...) - elseif e7 == "computerAvailable" or e7 == "computerAvailableRedirect" then - for proxy in fs.mounts() do - if proxy.MineOSNetworkModem and proxy.address == e3 then - fs.umount(proxy) + if system.properties.networkEnabled then + network.eventHandlerID = event.addHandler(function(...) + local eventData = {...} + + if (eventData[1] == "component_added" or eventData[1] == "component_removed") and (eventData[3] == "modem" or eventData[3] == "internet") then + network.updateComponents() + elseif eventData[1] == "modem_message" and system.properties.networkEnabled and eventData[6] == "network" then + if eventData[7] == "request" then + handleRequest(eventData) + elseif eventData[7] == "computerAvailable" or eventData[7] == "computerAvailableRedirect" then + for proxy in filesystem.mounts() do + if proxy.networkModem and proxy.address == eventData[3] then + filesystem.unmount(proxy) + end end + + proxy = newModemProxy(eventData[3]) + proxy.name = eventData[8] + filesystem.mount(proxy, paths.system.mounts .. eventData[3] .. "/") + + if eventData[7] == "computerAvailable" then + network.sendMessage(eventData[3], "network", "computerAvailableRedirect", system.properties.networkName) + end + + if not system.properties.networkUsers[eventData[3]] then + system.properties.networkUsers[eventData[3]] = {} + system.saveProperties() + end + + computer.pushSignal("network", "updateProxyList") + elseif eventData[7] == "computerNotAvailable" then + local proxy = network.getMountedModemProxy(eventData[3]) + if proxy then + filesystem.unmount(proxy) + end + + computer.pushSignal("network", "updateProxyList") end - - proxy = newModemProxy(e3) - proxy.name = e8 - fs.mount(proxy, MineOSNetwork.mountPaths.modem .. e3 .. "/") - - if e7 == "computerAvailable" then - MineOSNetwork.sendMessage(e3, "MineOSNetwork", "computerAvailableRedirect", MineOSCore.properties.network.name) - end - - if not MineOSCore.properties.network.users[e3] then - MineOSCore.properties.network.users[e3] = {} - MineOSCore.saveProperties() - end - - computer.pushSignal("MineOSNetwork", "updateProxyList") - elseif e7 == "computerNotAvailable" then - local proxy = MineOSNetwork.getMountedModemProxy(e3) - if proxy then - fs.umount(proxy) - end - - computer.pushSignal("MineOSNetwork", "updateProxyList") end - end - end, - math.huge, - math.huge -) + end) + end +end + +function network.disable() + system.properties.networkEnabled = false + system.saveProperties() + network.update() +end + +function network.enable() + system.properties.networkEnabled = true + system.saveProperties() + network.update() +end ---------------------------------------------------------------------------------------------------------------- -return MineOSNetwork +-- network.updateComponents() + +-- local proxy, reason = network.FTPProxy("localhost", 8888, "root", "1234") +-- print(proxy, reason) + +---------------------------------------------------------------------------------------------------------------- + +return network + + + + + diff --git a/Libraries/Number.lua b/Libraries/Number.lua new file mode 100755 index 00000000..c5ea0de5 --- /dev/null +++ b/Libraries/Number.lua @@ -0,0 +1,36 @@ + +local number = {} + +-------------------------------------------------------------------------------- + +function number.round(num) + if num >= 0 then + return math.floor(num + 0.5) + else + return math.ceil(num - 0.5) + end +end + +function number.roundToDecimalPlaces(num, decimalPlaces) + local mult = 10 ^ (decimalPlaces or 0) + return number.round(num * mult) / mult +end + +function number.getDigitCount(num) + return num == 0 and 1 or math.ceil(math.log(num + 1, 10)) +end + +function number.shorten(num, digitCount) + if num < 1000 then + return num + else + local shortcuts = { "K", "M", "G", "T", "P", "E", "Z", "Y" } + local index = math.floor(math.log(num, 1000)) + + return number.roundToDecimalPlaces(num / 1000 ^ index, digitCount) .. shortcuts[index] + end +end + +-------------------------------------------------------------------------------- + +return number diff --git a/lib/OpenComputersGL/Main.lua b/Libraries/OpenComputersGL/Main.lua similarity index 97% rename from lib/OpenComputersGL/Main.lua rename to Libraries/OpenComputersGL/Main.lua index 6d84dce7..543fb7f0 100755 --- a/lib/OpenComputersGL/Main.lua +++ b/Libraries/OpenComputersGL/Main.lua @@ -1,9 +1,9 @@ -------------------------------------------------------- Libraries -------------------------------------------------------- -local color = require("color") -local vector = require("vector") -local buffer = require("doubleBuffering") +local color = require("Color") +local vector = require("Vector") +local screen = require("Screen") local materials = require("OpenComputersGL/Materials") local renderer = require("OpenComputersGL/Renderer") local OCGL = {} @@ -142,7 +142,7 @@ end function OCGL.clearBuffer(backgroundColor) OCGL.nextVertexIndex, OCGL.vertices, OCGL.triangles, OCGL.lines, OCGL.floatingTexts, OCGL.lights = 1, {}, {}, {}, {}, {} renderer.clearDepthBuffer() - buffer.clear(backgroundColor) + screen.clear(backgroundColor) end function OCGL.createPerspectiveProjection() @@ -164,24 +164,24 @@ function OCGL.getTriangleLightIntensity(vertex1, vertex2, vertex3, indexedLight) if lightDistance <= indexedLight[3] then local normalVector = vector.getSurfaceNormal(vertex1, vertex2, vertex3) - -- buffer.drawText(2, buffer.height - 2, 0x0, "normalVector: " .. normalVector[1] .. " x " .. normalVector[2] .. " x " .. normalVector[3]) + -- screen.drawText(2, screen.height - 2, 0x0, "normalVector: " .. normalVector[1] .. " x " .. normalVector[2] .. " x " .. normalVector[3]) local cameraScalar = vector.scalarMultiply({0, 0, 100}, normalVector) local lightScalar = vector.scalarMultiply(lightVector, normalVector ) - -- buffer.drawText(2, buffer.height - 1, 0xFFFFFF, "Scalars: " .. cameraScalar .. " x " .. lightScalar) + -- screen.drawText(2, screen.height - 1, 0xFFFFFF, "Scalars: " .. cameraScalar .. " x " .. lightScalar) if cameraScalar < 0 and lightScalar >= 0 or cameraScalar >= 0 and lightScalar < 0 then local absAngle = math.abs(math.acos(lightScalar / (lightDistance * vector.length(normalVector)))) if absAngle > 1.5707963267949 then absAngle = 3.1415926535898 - absAngle end - -- buffer.drawText(2, buffer.height, 0xFFFFFF, "Angle: " .. math.deg(angle) .. ", newAngle: " .. math.deg(absAngle) .. ", intensity: " .. absAngle / 1.5707963267949) + -- screen.drawText(2, screen.height, 0xFFFFFF, "Angle: " .. math.deg(angle) .. ", newAngle: " .. math.deg(absAngle) .. ", intensity: " .. absAngle / 1.5707963267949) return indexedLight[2] * (1 - lightDistance / indexedLight[3]) * (1 - absAngle / 1.5707963267949) else return 0 end else - -- buffer.drawText(2, buffer.height, 0x0, "Out of light range: " .. lightDistance .. " vs " .. indexedLight[2]) + -- screen.drawText(2, screen.height, 0x0, "Out of light range: " .. lightDistance .. " vs " .. indexedLight[2]) return 0 end end diff --git a/lib/OpenComputersGL/Materials.lua b/Libraries/OpenComputersGL/Materials.lua similarity index 97% rename from lib/OpenComputersGL/Materials.lua rename to Libraries/OpenComputersGL/Materials.lua index fc09ba0c..5b22c2d2 100755 --- a/lib/OpenComputersGL/Materials.lua +++ b/Libraries/OpenComputersGL/Materials.lua @@ -1,5 +1,5 @@ -local color = require("color") +local color = require("Color") local materials = {} ------------------------------------------------------------------------------------------------------------------------ diff --git a/lib/OpenComputersGL/Renderer.lua b/Libraries/OpenComputersGL/Renderer.lua similarity index 92% rename from lib/OpenComputersGL/Renderer.lua rename to Libraries/OpenComputersGL/Renderer.lua index 781d7cb1..a1ed4822 100755 --- a/lib/OpenComputersGL/Renderer.lua +++ b/Libraries/OpenComputersGL/Renderer.lua @@ -1,11 +1,9 @@ -------------------------------------------------------- Libraries -------------------------------------------------------- -local computer = require("computer") -local vector = require("vector") -local unicode = require("unicode") +local vector = require("Vector") local materials = require("OpenComputersGL/Materials") -local buffer = require("doubleBuffering") +local screen = require("Screen") local renderer = { depthBuffer = {}, @@ -43,8 +41,8 @@ function renderer.setPixelUsingDepthBuffer(x, y, pixelDepthValue, pixelColor) then if pixelDepthValue < renderer.depthBuffer[y][x] then renderer.depthBuffer[y][x] = pixelDepthValue - buffer.semiPixelRawSet(buffer.getIndex(x, math.ceil(y / 2)), pixelColor, y % 2 == 0) - -- buffer.set(x, y, pixelColor, 0x0, " ") + screen.semiPixelRawSet(screen.getIndex(x, math.ceil(y / 2)), pixelColor, y % 2 == 0) + -- screen.set(x, y, pixelColor, 0x0, " ") end end end @@ -77,7 +75,7 @@ function renderer.visualizeDepthBuffer() for x = 1, #renderer.depthBuffer[y] do local value = (renderer.depthBuffer[y][x] - math.abs(minDepth)) / delta local color = grayscalePalette[math.floor(#grayscalePalette * value)] - buffer.semiPixelSet(x, y, color or 0x0) + screen.semiPixelSet(x, y, color or 0x0) end end end @@ -202,7 +200,7 @@ local function fillTexturedPart(firstZ, secondZ, x1Screen, x2Screen, z1Screen, z local u, uStep = u1Texture, (u2Texture - u1Texture) / (x2Screen - x1Screen) local v, vStep = v1Texture, (v2Texture - v1Texture) / (x2Screen - x1Screen) - -- buffer.drawText(1, 1, 0xFF00FF, "GOVNO: " .. math.abs(renderer.viewport.projectionSurface / z)) + -- screen.drawText(1, 1, 0xFF00FF, "GOVNO: " .. math.abs(renderer.viewport.projectionSurface / z)) local color, uVal, vVal for x = math.floor(x1Screen), math.floor(x2Screen) do @@ -213,7 +211,7 @@ local function fillTexturedPart(firstZ, secondZ, x1Screen, x2Screen, z1Screen, z color = 0x00FF00 end renderer.setPixelUsingDepthBuffer(x, y, z, color) - -- buffer.semiPixelSet(x, y, color) + -- screen.semiPixelSet(x, y, color) z, u, v = z + zStep, u + uStep, v + vStep end end @@ -246,7 +244,7 @@ function renderer.renderTexturedTriangle(points, texture) end -- for i = 1, 3 do - -- buffer.drawText(math.floor(points[i][1]), math.floor(points[i][2]), 0xFFFFFF, "ID " .. i .. ": u = " .. points[i][4] .. ", v = " .. points[topID][5]) + -- screen.drawText(math.floor(points[i][1]), math.floor(points[i][2]), 0xFFFFFF, "ID " .. i .. ": u = " .. points[i][4] .. ", v = " .. points[topID][5]) -- end end @@ -271,9 +269,9 @@ function renderer.renderFloatingText(x, y, z, color, text) end end - index = buffer.getIndex(x, yInteger) - background = buffer.rawGet(index) - buffer.rawSet(index, background, color, unicode.sub(text, i, i)) + index = screen.getIndex(x, yInteger) + background = screen.rawGet(index) + screen.rawSet(index, background, color, unicode.sub(text, i, i)) end end x = x + 1 @@ -285,19 +283,19 @@ end local function drawSegments(x, y, segments, color) for i = 1, #segments do if segments[i] == 1 then - buffer.drawSemiPixelRectangle(x, y, 3, 1, color) + screen.drawSemiPixelRectangle(x, y, 3, 1, color) elseif segments[i] == 2 then - buffer.drawSemiPixelRectangle(x + 2, y, 1, 3, color) + screen.drawSemiPixelRectangle(x + 2, y, 1, 3, color) elseif segments[i] == 3 then - buffer.drawSemiPixelRectangle(x + 2, y + 2, 1, 3, color) + screen.drawSemiPixelRectangle(x + 2, y + 2, 1, 3, color) elseif segments[i] == 4 then - buffer.drawSemiPixelRectangle(x, y + 4, 3, 1, color) + screen.drawSemiPixelRectangle(x, y + 4, 3, 1, color) elseif segments[i] == 5 then - buffer.drawSemiPixelRectangle(x, y + 2, 1, 3, color) + screen.drawSemiPixelRectangle(x, y + 2, 1, 3, color) elseif segments[i] == 6 then - buffer.drawSemiPixelRectangle(x, y, 1, 3, color) + screen.drawSemiPixelRectangle(x, y, 1, 3, color) elseif segments[i] == 7 then - buffer.drawSemiPixelRectangle(x, y + 2, 3, 1, color) + screen.drawSemiPixelRectangle(x, y + 2, 3, 1, color) else error("Че за говно ты сюда напихал? Переделывай!") end @@ -326,8 +324,8 @@ end ------------------------------------------------------------------------------------------------------------------------ --- buffer.start() --- buffer.clear(0xFFFFFF) +-- screen.start() +-- screen.clear(0xFFFFFF) -- local texture = materials.newDebugTexture(16, 16, 0xFF00FF, 0x000000) -- renderer.renderTexturedTriangle({ @@ -341,7 +339,7 @@ end -- {52, 52, 1, 16, 16}, -- }, texture) --- buffer.drawChanges(true) +-- screen.update(true) ------------------------------------------------------------------------------------------------------------------------ diff --git a/Libraries/Paths.lua b/Libraries/Paths.lua new file mode 100755 index 00000000..c4b6f42f --- /dev/null +++ b/Libraries/Paths.lua @@ -0,0 +1,57 @@ + +local paths = {system = {}, user = {}} + +-------------------------------------------------------------------------------- + +paths.system.libraries = "/Libraries/" +paths.system.applications = "/Applications/" +paths.system.icons = "/Icons/" +paths.system.localizations = "/Localizations/" +paths.system.extensions = "/Extensions/" +paths.system.mounts = "/Mounts/" +paths.system.temporary = "/Temporary/" +paths.system.pictures = "/Pictures/" +paths.system.screensavers = "/Screensavers/" +paths.system.users = "/Users/" + +paths.system.applicationSample = paths.system.applications .. "Sample.app/" +paths.system.applicationAppMarket = paths.system.applications .. "App Market.app/Main.lua" +paths.system.applicationMineCodeIDE = paths.system.applications .. "MineCode IDE.app/Main.lua" +paths.system.applicationFinder = paths.system.applications .. "Finder.app/Main.lua" +paths.system.applicationPictureEdit = paths.system.applications .. "Picture Edit.app/Main.lua" +paths.system.applicationSettings = paths.system.applications .. "Settings.app/Main.lua" + +-------------------------------------------------------------------------------- + +function paths.create(what) + for _, path in pairs(what) do + if path:sub(-1, -1) == "/" then + filesystem.makeDirectory(path) + end + end +end + +function paths.getUser(name) + local user = {} + + user.home = paths.system.users .. name .. "/" + user.applicationData = user.home .. "Application data/" + user.desktop = user.home .. "Desktop/" + user.libraries = user.home .. "Libraries/" + user.applications = user.home .. "Applications/" + user.pictures = user.home .. "Pictures/" + user.screensavers = user.home .. "Screensavers/" + user.trash = user.home .. "Trash/" + user.versions = user.home .. "Versions.cfg" + user.properties = user.home .. "Properties.cfg" + + return user +end + +function paths.updateUser(...) + paths.user = paths.getUser(...) +end + +-------------------------------------------------------------------------------- + +return paths diff --git a/Libraries/SHA-256.lua b/Libraries/SHA-256.lua new file mode 100755 index 00000000..b6a45ac9 --- /dev/null +++ b/Libraries/SHA-256.lua @@ -0,0 +1,202 @@ + +-- +-- Adaptation of the Secure Hashing Algorithm (SHA-244/256) +-- Found Here: http://lua-users.org/wiki/SecureHashAlgorithm +-- +-- Using an adapted version of the bit library +-- Found Here: https://bitbucket.org/Boolsheet/bslf/src/1ee664885805/bit.lua +-- + +local MOD = 2^32 +local MODM = MOD-1 + +local function memoize(f) + local mt = {} + local t = setmetatable({}, mt) + function mt:__index(k) + local v = f(k) + t[k] = v + return v + end + return t +end + +local function make_bitop_uncached(t, m) + local function bitop(a, b) + local res,p = 0,1 + while a ~= 0 and b ~= 0 do + local am, bm = a % m, b % m + res = res + t[am][bm] * p + a = (a - am) / m + b = (b - bm) / m + p = p*m + end + res = res + (a + b) * p + return res + end + return bitop +end + +local function make_bitop(t) + local op1 = make_bitop_uncached(t,2^1) + local op2 = memoize(function(a) return memoize(function(b) return op1(a, b) end) end) + return make_bitop_uncached(op2, 2 ^ (t.n or 1)) +end + +local bxor1 = make_bitop({[0] = {[0] = 0,[1] = 1}, [1] = {[0] = 1, [1] = 0}, n = 4}) + +local function bxor(a, b, c, ...) + local z = nil + if b then + a = a % MOD + b = b % MOD + z = bxor1(a, b) + if c then z = bxor(z, c, ...) end + return z + elseif a then + return a % MOD + else + return 0 + end +end + +local function band(a, b, c, ...) + local z + if b then + a = a % MOD + b = b % MOD + z = ((a + b) - bxor1(a,b)) / 2 + if c then + z = bit32_band(z, c, ...) + end + + return z + elseif a then + return a % MOD + else + return MODM + end +end + +local function bnot(x) return (-1 - x) % MOD end + +local function rshift1(a, disp) + if disp < 0 then return lshift(a,-disp) end + return math.floor(a % 2 ^ 32 / 2 ^ disp) +end + +local function rshift(x, disp) + if disp > 31 or disp < -31 then return 0 end + return rshift1(x % MOD, disp) +end + +local function lshift(a, disp) + if disp < 0 then return rshift(a,-disp) end + return (a * 2 ^ disp) % 2 ^ 32 +end + +local function rrotate(x, disp) + x = x % MOD + disp = disp % 32 + local low = band(x, 2 ^ disp - 1) + return rshift(x, disp) + lshift(low, 32 - disp) +end + +local function str2hexa(s) + return (string.gsub(s, ".", function(c) return string.format("%02x", string.byte(c)) end)) +end + +local function num2s(l, n) + local s = "" + for i = 1, n do + local rem = l % 256 + s = string.char(rem) .. s + l = (l - rem) / 256 + end + return s +end + +local function s232num(s, i) + local n = 0 + for i = i, i + 3 do n = n*256 + string.byte(s, i) end + return n +end + +local function preproc(msg, len) + local extra = 64 - ((len + 9) % 64) + len = num2s(8 * len, 8) + msg = msg .. "\128" .. string.rep("\0", extra) .. len + assert(#msg % 64 == 0) + return msg +end + +local function initH256(H) + H[1] = 0x6a09e667 + H[2] = 0xbb67ae85 + H[3] = 0x3c6ef372 + H[4] = 0xa54ff53a + H[5] = 0x510e527f + H[6] = 0x9b05688c + H[7] = 0x1f83d9ab + H[8] = 0x5be0cd19 + return H +end + +local function digestblock(msg, i, H) + local k = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, + } + + local w = {} + for j = 1, 16 do w[j] = s232num(msg, i + (j - 1)*4) end + for j = 17, 64 do + local v = w[j - 15] + local s0 = bxor(rrotate(v, 7), rrotate(v, 18), rshift(v, 3)) + v = w[j - 2] + w[j] = w[j - 16] + s0 + w[j - 7] + bxor(rrotate(v, 17), rrotate(v, 19), rshift(v, 10)) + end + + local a, b, c, d, e, f, g, h = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] + for i = 1, 64 do + local s0 = bxor(rrotate(a, 2), rrotate(a, 13), rrotate(a, 22)) + local maj = bxor(band(a, b), band(a, c), band(b, c)) + local t2 = s0 + maj + local s1 = bxor(rrotate(e, 6), rrotate(e, 11), rrotate(e, 25)) + local ch = bxor (band(e, f), band(bnot(e), g)) + local t1 = h + s1 + ch + k[i] + w[i] + h, g, f, e, d, c, b, a = g, f, e, d + t1, c, b, a, t1 + t2 + end + + H[1] = band(H[1] + a) + H[2] = band(H[2] + b) + H[3] = band(H[3] + c) + H[4] = band(H[4] + d) + H[5] = band(H[5] + e) + H[6] = band(H[6] + f) + H[7] = band(H[7] + g) + H[8] = band(H[8] + h) +end + +return { + hash = function(msg) + msg = preproc(msg, #msg) + local H = initH256({}) + for i = 1, #msg, 64 do digestblock(msg, i, H) end + return str2hexa(num2s(H[1], 4) .. num2s(H[2], 4) .. num2s(H[3], 4) .. num2s(H[4], 4) .. num2s(H[5], 4) .. num2s(H[6], 4) .. num2s(H[7], 4) .. num2s(H[8], 4)) + end +} \ No newline at end of file diff --git a/Libraries/Screen.lua b/Libraries/Screen.lua new file mode 100755 index 00000000..3d6ca0b5 --- /dev/null +++ b/Libraries/Screen.lua @@ -0,0 +1,631 @@ + +local color = require("Color") +local image = require("Image") + +-------------------------------------------------------------------------------- + +local bufferWidth, bufferHeight +local currentFrameBackgrounds, currentFrameForegrounds, currentFrameSymbols, newFrameBackgrounds, newFrameForegrounds, newFrameSymbols +local drawLimitX1, drawLimitX2, drawLimitY1, drawLimitY2 +local GPUProxy, GPUProxyGetResolution, GPUProxySetResolution, GPUProxyGetBackground, GPUProxyGetForeground, GPUProxySetBackground, GPUProxySetForeground, GPUProxyGet, GPUProxySet, GPUProxyFill + +local mathCeil, mathFloor, mathModf, mathAbs = math.ceil, math.floor, math.modf, math.abs +local tableInsert, tableConcat = table.insert, table.concat +local colorBlend = color.blend +local unicodeLen, unicodeSub = unicode.len, unicode.sub + +-------------------------------------------------------------------------------- + +local function getIndex(x, y) + return bufferWidth * (y - 1) + x +end + +local function getCurrentFrameTables() + return currentFrameBackgrounds, currentFrameForegrounds, currentFrameSymbols +end + +local function getNewFrameTables() + return newFrameBackgrounds, newFrameForegrounds, newFrameSymbols +end + +-------------------------------------------------------------------------------- + +local function setDrawLimit(x1, y1, x2, y2) + drawLimitX1, drawLimitY1, drawLimitX2, drawLimitY2 = x1, y1, x2, y2 +end + +local function resetDrawLimit() + drawLimitX1, drawLimitY1, drawLimitX2, drawLimitY2 = 1, 1, bufferWidth, bufferHeight +end + +local function getDrawLimit() + return drawLimitX1, drawLimitY1, drawLimitX2, drawLimitY2 +end + +-------------------------------------------------------------------------------- + +local function flush(width, height) + if not width or not height then + width, height = GPUProxyGetResolution() + end + + currentFrameBackgrounds, currentFrameForegrounds, currentFrameSymbols, newFrameBackgrounds, newFrameForegrounds, newFrameSymbols = {}, {}, {}, {}, {}, {} + bufferWidth = width + bufferHeight = height + resetDrawLimit() + + for y = 1, bufferHeight do + for x = 1, bufferWidth do + tableInsert(currentFrameBackgrounds, 0x010101) + tableInsert(currentFrameForegrounds, 0xFEFEFE) + tableInsert(currentFrameSymbols, " ") + + tableInsert(newFrameBackgrounds, 0x010101) + tableInsert(newFrameForegrounds, 0xFEFEFE) + tableInsert(newFrameSymbols, " ") + end + end +end + +local function setResolution(width, height) + GPUProxySetResolution(width, height) + flush(width, height) +end + +local function getResolution() + return bufferWidth, bufferHeight +end + +local function getWidth() + return bufferWidth +end + +local function getHeight() + return bufferHeight +end + +local function bind(...) + GPUProxy.bind(...) + flush(GPUProxyGetResolution()) +end + +local function getGPUProxy() + return GPUProxy +end + +local function updateGPUProxyMethods() + GPUProxyGet = GPUProxy.get + GPUProxyGetResolution = GPUProxy.getResolution + GPUProxyGetBackground = GPUProxy.getBackground + GPUProxyGetForeground = GPUProxy.getForeground + + GPUProxySet = GPUProxy.set + GPUProxySetResolution = GPUProxy.setResolution + GPUProxySetBackground = GPUProxy.setBackground + GPUProxySetForeground = GPUProxy.setForeground + + GPUProxyFill = GPUProxy.fill +end + +local function setGPUProxy(proxy) + GPUProxy = proxy + updateGPUProxyMethods() + flush(GPUProxyGetResolution()) +end + +local function getScaledResolution(scale) + if not scale or scale > 1 then + scale = 1 + elseif scale < 0.1 then + scale = 0.1 + end + + local aspectWidth, aspectHeight = component.proxy(GPUProxy.getScreen()).getAspectRatio() + local maxWidth, maxHeight = GPUProxy.maxResolution() + local proportion = (aspectWidth * 2 - 0.5) / (aspectHeight - 0.25) + + local height = scale * math.min( + maxWidth / proportion, + maxWidth, + math.sqrt(maxWidth * maxHeight / proportion) + ) + + return math.floor(height * proportion), math.floor(height) +end + +-------------------------------------------------------------------------------- + +local function rawSet(index, background, foreground, symbol) + newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = background, foreground, symbol +end + +local function rawGet(index) + return newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] +end + +local function get(x, y) + if x >= 1 and y >= 1 and x <= bufferWidth and y <= bufferHeight then + local index = bufferWidth * (y - 1) + x + return newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] + else + return 0x000000, 0x000000, " " + end +end + +local function set(x, y, background, foreground, symbol) + if x >= drawLimitX1 and y >= drawLimitY1 and x <= drawLimitX2 and y <= drawLimitY2 then + local index = bufferWidth * (y - 1) + x + newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = background, foreground, symbol + end +end + +local function drawRectangle(x, y, width, height, background, foreground, symbol, transparency) + local index, indexStepOnReachOfSquareWidth = bufferWidth * (y - 1) + x, bufferWidth - width + for j = y, y + height - 1 do + if j >= drawLimitY1 and j <= drawLimitY2 then + for i = x, x + width - 1 do + if i >= drawLimitX1 and i <= drawLimitX2 then + if transparency then + newFrameBackgrounds[index], newFrameForegrounds[index] = + colorBlend(newFrameBackgrounds[index], background, transparency), + colorBlend(newFrameForegrounds[index], background, transparency) + else + newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = background, foreground, symbol + end + end + + index = index + 1 + end + + index = index + indexStepOnReachOfSquareWidth + else + index = index + bufferWidth + end + end +end + +local function clear(color, transparency) + drawRectangle(1, 1, bufferWidth, bufferHeight, color or 0x0, 0x000000, " ", transparency) +end + +local function copy(x, y, width, height) + local copyArray, index = { width, height } + + for j = y, y + height - 1 do + for i = x, x + width - 1 do + if i >= 1 and j >= 1 and i <= bufferWidth and j <= bufferHeight then + index = bufferWidth * (j - 1) + i + tableInsert(copyArray, newFrameBackgrounds[index]) + tableInsert(copyArray, newFrameForegrounds[index]) + tableInsert(copyArray, newFrameSymbols[index]) + else + tableInsert(copyArray, 0x0) + tableInsert(copyArray, 0x0) + tableInsert(copyArray, " ") + end + end + end + + return copyArray +end + +local function paste(startX, startY, picture) + local imageWidth = picture[1] + local bufferIndex, pictureIndex, bufferIndexStepOnReachOfImageWidth = bufferWidth * (startY - 1) + startX, 3, bufferWidth - imageWidth + + for y = startY, startY + picture[2] - 1 do + if y >= drawLimitY1 and y <= drawLimitY2 then + for x = startX, startX + imageWidth - 1 do + if x >= drawLimitX1 and x <= drawLimitX2 then + newFrameBackgrounds[bufferIndex] = picture[pictureIndex] + newFrameForegrounds[bufferIndex] = picture[pictureIndex + 1] + newFrameSymbols[bufferIndex] = picture[pictureIndex + 2] + end + + bufferIndex, pictureIndex = bufferIndex + 1, pictureIndex + 3 + end + + bufferIndex = bufferIndex + bufferIndexStepOnReachOfImageWidth + else + bufferIndex, pictureIndex = bufferIndex + bufferWidth, pictureIndex + imageWidth * 3 + end + end +end + +local function rasterizeLine(x1, y1, x2, y2, method) + local inLoopValueFrom, inLoopValueTo, outLoopValueFrom, outLoopValueTo, isReversed, inLoopValueDelta, outLoopValueDelta = x1, x2, y1, y2, false, mathAbs(x2 - x1), mathAbs(y2 - y1) + if inLoopValueDelta < outLoopValueDelta then + inLoopValueFrom, inLoopValueTo, outLoopValueFrom, outLoopValueTo, isReversed, inLoopValueDelta, outLoopValueDelta = y1, y2, x1, x2, true, outLoopValueDelta, inLoopValueDelta + end + + if outLoopValueFrom > outLoopValueTo then + outLoopValueFrom, outLoopValueTo = outLoopValueTo, outLoopValueFrom + inLoopValueFrom, inLoopValueTo = inLoopValueTo, inLoopValueFrom + end + + local outLoopValue, outLoopValueCounter, outLoopValueTriggerIncrement = outLoopValueFrom, 1, inLoopValueDelta / outLoopValueDelta + local outLoopValueTrigger = outLoopValueTriggerIncrement + for inLoopValue = inLoopValueFrom, inLoopValueTo, inLoopValueFrom < inLoopValueTo and 1 or -1 do + if isReversed then + method(outLoopValue, inLoopValue) + else + method(inLoopValue, outLoopValue) + end + + outLoopValueCounter = outLoopValueCounter + 1 + if outLoopValueCounter > outLoopValueTrigger then + outLoopValue, outLoopValueTrigger = outLoopValue + 1, outLoopValueTrigger + outLoopValueTriggerIncrement + end + end +end + +local function rasterizeEllipse(centerX, centerY, radiusX, radiusY, method) + local function rasterizeEllipsePoints(XP, YP) + method(centerX + XP, centerY + YP) + method(centerX - XP, centerY + YP) + method(centerX - XP, centerY - YP) + method(centerX + XP, centerY - YP) + end + + local x, y, changeX, changeY, ellipseError, twoASquare, twoBSquare = radiusX, 0, radiusY * radiusY * (1 - 2 * radiusX), radiusX * radiusX, 0, 2 * radiusX * radiusX, 2 * radiusY * radiusY + local stoppingX, stoppingY = twoBSquare * radiusX, 0 + + while stoppingX >= stoppingY do + rasterizeEllipsePoints(x, y) + + y, stoppingY, ellipseError = y + 1, stoppingY + twoASquare, ellipseError + changeY + changeY = changeY + twoASquare + + if (2 * ellipseError + changeX) > 0 then + x, stoppingX, ellipseError = x - 1, stoppingX - twoBSquare, ellipseError + changeX + changeX = changeX + twoBSquare + end + end + + x, y, changeX, changeY, ellipseError, stoppingX, stoppingY = 0, radiusY, radiusY * radiusY, radiusX * radiusX * (1 - 2 * radiusY), 0, 0, twoASquare * radiusY + + while stoppingX <= stoppingY do + rasterizeEllipsePoints(x, y) + + x, stoppingX, ellipseError = x + 1, stoppingX + twoBSquare, ellipseError + changeX + changeX = changeX + twoBSquare + + if (2 * ellipseError + changeY) > 0 then + y, stoppingY, ellipseError = y - 1, stoppingY - twoASquare, ellipseError + changeY + changeY = changeY + twoASquare + end + end +end + +local function drawLine(x1, y1, x2, y2, background, foreground, symbol) + rasterizeLine(x1, y1, x2, y2, function(x, y) + set(x, y, background, foreground, symbol) + end) +end + +local function drawEllipse(centerX, centerY, radiusX, radiusY, background, foreground, symbol) + rasterizeEllipse(centerX, centerY, radiusX, radiusY, function(x, y) + set(x, y, background, foreground, symbol) + end) +end + +local function drawText(x, y, textColor, data, transparency) + if y >= drawLimitY1 and y <= drawLimitY2 then + local charIndex, bufferIndex = 1, bufferWidth * (y - 1) + x + + for charIndex = 1, unicodeLen(data) do + if x >= drawLimitX1 and x <= drawLimitX2 then + if transparency then + newFrameForegrounds[bufferIndex] = colorBlend(newFrameBackgrounds[bufferIndex], textColor, transparency) + else + newFrameForegrounds[bufferIndex] = textColor + end + + newFrameSymbols[bufferIndex] = unicodeSub(data, charIndex, charIndex) + end + + x, bufferIndex = x + 1, bufferIndex + 1 + end + end +end + +local function drawImage(startX, startY, picture, blendForeground) + local bufferIndex, pictureIndex, imageWidth, background, foreground, alpha, symbol = bufferWidth * (startY - 1) + startX, 3, picture[1] + local bufferIndexStepOnReachOfImageWidth = bufferWidth - imageWidth + + for y = startY, startY + picture[2] - 1 do + if y >= drawLimitY1 and y <= drawLimitY2 then + for x = startX, startX + imageWidth - 1 do + if x >= drawLimitX1 and x <= drawLimitX2 then + alpha, symbol = picture[pictureIndex + 2], picture[pictureIndex + 3] + + -- If it's fully transparent pixel + if alpha == 0 then + newFrameBackgrounds[bufferIndex], newFrameForegrounds[bufferIndex] = picture[pictureIndex], picture[pictureIndex + 1] + -- If it has some transparency + elseif alpha > 0 and alpha < 1 then + newFrameBackgrounds[bufferIndex] = colorBlend(newFrameBackgrounds[bufferIndex], picture[pictureIndex], alpha) + + if blendForeground then + newFrameForegrounds[bufferIndex] = colorBlend(newFrameForegrounds[bufferIndex], picture[pictureIndex + 1], alpha) + else + newFrameForegrounds[bufferIndex] = picture[pictureIndex + 1] + end + -- If it's not transparent with whitespace + elseif symbol ~= " " then + newFrameForegrounds[bufferIndex] = picture[pictureIndex + 1] + end + + newFrameSymbols[bufferIndex] = symbol + end + + bufferIndex, pictureIndex = bufferIndex + 1, pictureIndex + 4 + end + + bufferIndex = bufferIndex + bufferIndexStepOnReachOfImageWidth + else + bufferIndex, pictureIndex = bufferIndex + bufferWidth, pictureIndex + imageWidth * 4 + end + end +end + +local function drawFrame(x, y, width, height, color) + local stringUp, stringDown, x2 = "┌" .. string.rep("─", width - 2) .. "┐", "└" .. string.rep("─", width - 2) .. "┘", x + width - 1 + + drawText(x, y, color, stringUp); y = y + 1 + for i = 1, height - 2 do + drawText(x, y, color, "│") + drawText(x2, y, color, "│") + y = y + 1 + end + drawText(x, y, color, stringDown) +end + +-------------------------------------------------------------------------------- + +local function semiPixelRawSet(index, color, yPercentTwoEqualsZero) + local upperPixel, lowerPixel, bothPixel = "▀", "▄", " " + local background, foreground, symbol = newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] + + if yPercentTwoEqualsZero then + if symbol == upperPixel then + if color == foreground then + newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = color, foreground, bothPixel + else + newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = color, foreground, symbol + end + elseif symbol == bothPixel then + if color ~= background then + newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = background, color, lowerPixel + end + else + newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = background, color, lowerPixel + end + else + if symbol == lowerPixel then + if color == foreground then + newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = color, foreground, bothPixel + else + newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = color, foreground, symbol + end + elseif symbol == bothPixel then + if color ~= background then + newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = background, color, upperPixel + end + else + newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] = background, color, upperPixel + end + end +end + +local function semiPixelSet(x, y, color) + local yFixed = mathCeil(y / 2) + if x >= drawLimitX1 and yFixed >= drawLimitY1 and x <= drawLimitX2 and yFixed <= drawLimitY2 then + semiPixelRawSet(bufferWidth * (yFixed - 1) + x, color, y % 2 == 0) + end +end + +local function drawSemiPixelRectangle(x, y, width, height, color) + local index, indexStepForward, indexStepBackward, jPercentTwoEqualsZero, jFixed = bufferWidth * (mathCeil(y / 2) - 1) + x, (bufferWidth - width), width + for j = y, y + height - 1 do + jPercentTwoEqualsZero = j % 2 == 0 + + for i = x, x + width - 1 do + jFixed = mathCeil(j / 2) + semiPixelRawSet(index, color, jPercentTwoEqualsZero) + index = index + 1 + end + + if jPercentTwoEqualsZero then + index = index + indexStepForward + else + index = index - indexStepBackward + end + end +end + +local function drawSemiPixelLine(x1, y1, x2, y2, color) + rasterizeLine(x1, y1, x2, y2, function(x, y) + semiPixelSet(x, y, color) + end) +end + +local function drawSemiPixelEllipse(centerX, centerY, radiusX, radiusY, color) + rasterizeEllipse(centerX, centerY, radiusX, radiusY, function(x, y) + semiPixelSet(x, y, color) + end) +end + +-------------------------------------------------------------------------------- + +local function getPointTimedPosition(firstPoint, secondPoint, time) + return { + x = firstPoint.x + (secondPoint.x - firstPoint.x) * time, + y = firstPoint.y + (secondPoint.y - firstPoint.y) * time + } +end + +local function getConnectionPoints(points, time) + local connectionPoints = {} + for point = 1, #points - 1 do + tableInsert(connectionPoints, getPointTimedPosition(points[point], points[point + 1], time)) + end + return connectionPoints +end + +local function getMainPointPosition(points, time) + if #points > 1 then + return getMainPointPosition(getConnectionPoints(points, time), time) + else + return points[1] + end +end + +local function drawSemiPixelCurve(points, color, precision) + local linePoints = {} + for time = 0, 1, precision or 0.01 do + tableInsert(linePoints, getMainPointPosition(points, time)) + end + + for point = 1, #linePoints - 1 do + drawSemiPixelLine(mathFloor(linePoints[point].x), mathFloor(linePoints[point].y), mathFloor(linePoints[point + 1].x), mathFloor(linePoints[point + 1].y), color) + end +end + +-------------------------------------------------------------------------------- + +local function update(force) + local index, indexStepOnEveryLine, changes = bufferWidth * (drawLimitY1 - 1) + drawLimitX1, (bufferWidth - drawLimitX2 + drawLimitX1 - 1), {} + local x, equalChars, equalCharsIndex, charX, charIndex, currentForeground + local currentFrameBackground, currentFrameForeground, currentFrameSymbol, changesCurrentFrameBackground, changesCurrentFrameBackgroundCurrentFrameForeground + + local changesCurrentFrameBackgroundCurrentFrameForegroundIndex + + for y = drawLimitY1, drawLimitY2 do + x = drawLimitX1 + while x <= drawLimitX2 do + -- Determine if some pixel data was changed (or if argument was passed) + if + currentFrameBackgrounds[index] ~= newFrameBackgrounds[index] or + currentFrameForegrounds[index] ~= newFrameForegrounds[index] or + currentFrameSymbols[index] ~= newFrameSymbols[index] or + force + then + -- Make pixel at both frames equal + currentFrameBackground, currentFrameForeground, currentFrameSymbol = newFrameBackgrounds[index], newFrameForegrounds[index], newFrameSymbols[index] + currentFrameBackgrounds[index] = currentFrameBackground + currentFrameForegrounds[index] = currentFrameForeground + currentFrameSymbols[index] = currentFrameSymbol + + -- Look for pixels with equal chars from right of current pixel + equalChars, equalCharsIndex, charX, charIndex = {currentFrameSymbol}, 2, x + 1, index + 1 + while charX <= drawLimitX2 do + -- Pixels becomes equal only if they have same background and (whitespace char or same foreground) + if + currentFrameBackground == newFrameBackgrounds[charIndex] and + ( + newFrameSymbols[charIndex] == " " or + currentFrameForeground == newFrameForegrounds[charIndex] + ) + then + -- Make pixel at both frames equal + currentFrameBackgrounds[charIndex] = newFrameBackgrounds[charIndex] + currentFrameForegrounds[charIndex] = newFrameForegrounds[charIndex] + currentFrameSymbols[charIndex] = newFrameSymbols[charIndex] + + equalChars[equalCharsIndex], equalCharsIndex = currentFrameSymbols[charIndex], equalCharsIndex + 1 + else + break + end + + charX, charIndex = charX + 1, charIndex + 1 + end + + -- Group pixels that need to be drawn by background and foreground + changes[currentFrameBackground] = changes[currentFrameBackground] or {} + changesCurrentFrameBackground = changes[currentFrameBackground] + changesCurrentFrameBackground[currentFrameForeground] = changesCurrentFrameBackground[currentFrameForeground] or {index = 1} + changesCurrentFrameBackgroundCurrentFrameForeground = changesCurrentFrameBackground[currentFrameForeground] + changesCurrentFrameBackgroundCurrentFrameForegroundIndex = changesCurrentFrameBackgroundCurrentFrameForeground.index + + changesCurrentFrameBackgroundCurrentFrameForeground[changesCurrentFrameBackgroundCurrentFrameForegroundIndex], changesCurrentFrameBackgroundCurrentFrameForegroundIndex = x, changesCurrentFrameBackgroundCurrentFrameForegroundIndex + 1 + changesCurrentFrameBackgroundCurrentFrameForeground[changesCurrentFrameBackgroundCurrentFrameForegroundIndex], changesCurrentFrameBackgroundCurrentFrameForegroundIndex = y, changesCurrentFrameBackgroundCurrentFrameForegroundIndex + 1 + changesCurrentFrameBackgroundCurrentFrameForeground[changesCurrentFrameBackgroundCurrentFrameForegroundIndex], changesCurrentFrameBackgroundCurrentFrameForegroundIndex = tableConcat(equalChars), changesCurrentFrameBackgroundCurrentFrameForegroundIndex + 1 + + x, index, changesCurrentFrameBackgroundCurrentFrameForeground.index = x + equalCharsIndex - 2, index + equalCharsIndex - 2, changesCurrentFrameBackgroundCurrentFrameForegroundIndex + end + + x, index = x + 1, index + 1 + end + + index = index + indexStepOnEveryLine + end + + -- Draw grouped pixels on screen + for background, foregrounds in pairs(changes) do + GPUProxySetBackground(background) + + for foreground, pixels in pairs(foregrounds) do + if currentForeground ~= foreground then + GPUProxySetForeground(foreground) + currentForeground = foreground + end + + for i = 1, #pixels, 3 do + GPUProxySet(pixels[i], pixels[i + 1], pixels[i + 2]) + end + end + end + + changes = nil +end + +-------------------------------------------------------------------------------- + +setGPUProxy(component.proxy(component.list("gpu")())) + +-------------------------------------------------------------------------------- + +return { + getCoordinates = getCoordinates, + getIndex = getIndex, + setDrawLimit = setDrawLimit, + resetDrawLimit = resetDrawLimit, + getDrawLimit = getDrawLimit, + flush = flush, + setResolution = setResolution, + bind = bind, + setGPUProxy = setGPUProxy, + getGPUProxy = getGPUProxy, + getScaledResolution = getScaledResolution, + getResolution = getResolution, + getWidth = getWidth, + getHeight = getHeight, + getCurrentFrameTables = getCurrentFrameTables, + getNewFrameTables = getNewFrameTables, + + rawSet = rawSet, + rawGet = rawGet, + get = get, + set = set, + clear = clear, + copy = copy, + paste = paste, + rasterizeLine = rasterizeLine, + rasterizeEllipse = rasterizeEllipse, + semiPixelRawSet = semiPixelRawSet, + semiPixelSet = semiPixelSet, + update = update, + + drawRectangle = drawRectangle, + drawLine = drawLine, + drawEllipse = drawEllipse, + drawText = drawText, + drawImage = drawImage, + drawFrame = drawFrame, + + drawSemiPixelRectangle = drawSemiPixelRectangle, + drawSemiPixelLine = drawSemiPixelLine, + drawSemiPixelEllipse = drawSemiPixelEllipse, + drawSemiPixelCurve = drawSemiPixelCurve, +} \ No newline at end of file diff --git a/Libraries/System.lua b/Libraries/System.lua new file mode 100755 index 00000000..cbbb04c8 --- /dev/null +++ b/Libraries/System.lua @@ -0,0 +1,2578 @@ + +local screen = require("Screen") +local filesystem = require("Filesystem") +local image = require("Image") +local color = require("Color") +local keyboard = require("Keyboard") +local event = require("Event") +local GUI = require("GUI") +local paths = require("Paths") +local text = require("Text") + +-------------------------------------------------------------------------------- + +local system = {} + +local iconImageWidth = 8 +local iconImageHeight = 4 +local iconCache = {} + +local bootUptime = computer.uptime() +local dateUptime = bootUptime +local screensaverUptime = bootUptime + +local user +local iconHalfWidth +local iconTextHeight +local iconImageHorizontalOffset +local bootRealTime + +local workspace +local windowsContainer +local dockContainer +local desktopMenu +local desktopMenuLayout +local desktopIconField +local desktopBackground +local desktopBackgroundColor = 0x1E1E1E +local desktopBackgroundWallpaper +local desktopBackgroundWallpaperX +local desktopBackgroundWallpaperY + +-------------------------------------------------------------------------------- + +-- Returns real timestamp in seconds +function system.getTime() + return bootRealTime + computer.uptime() + system.properties.timeTimezone +end + +-- Returns currently logged in user +function system.getUser() + return user +end + +-------------------------------------------------------------------------------- + +function system.saveProperties() + filesystem.writeTable(paths.user.properties, system.properties, true) +end + +function system.getCurrentScript() + local info + for runLevel = 0, math.huge do + info = debug.getinfo(runLevel) + if info then + if info.what == "main" then + return info.source:sub(2, -1) + end + else + error("Failed to get debug info for runlevel " .. runLevel) + end + end +end + +function system.getLocalization(pathToLocalizationFolder) + local required, english = pathToLocalizationFolder .. system.properties.localizationLanguage .. ".lang", pathToLocalizationFolder .. "English.lang" + + -- First trying to return required localization + if filesystem.exists(required) then + return filesystem.readTable(required) + -- Otherwise maybe english localization exists? + elseif filesystem.exists(english) then + return filesystem.readTable(english) + -- Otherwise returning first available localization + else + local list = filesystem.list(pathToLocalizationFolder) + if #list > 0 then + return filesystem.readTable(pathToLocalizationFolder .. list[1]) + else + error("Failed to get localization: directory is empty") + end + end +end + +function system.getCurrentScriptLocalization() + return system.getLocalization(filesystem.path(system.getCurrentScript()) .. "Localizations/") +end + +function system.getTemporaryPath() + local temporaryPath + repeat + temporaryPath = paths.system.temporary .. string.format("%08X", math.random(0xFFFFFFFF)) + until not filesystem.exists(temporaryPath) + + return temporaryPath +end + +function system.createShortcut(where, forWhat) + filesystem.makeDirectory(filesystem.path(where)) + filesystem.write(where, forWhat) +end + +function system.parseArguments(...) + local arguments, options = {...}, {} + local i = 1 + while i <= #arguments do + local option = arguments[i]:match("^%-+(.+)") + if option then + options[option] = true + table.remove(arguments, i) + end + + i = i + 1 + end + + return arguments, options +end + +function system.readShortcut(path) + local data, reason = filesystem.read(path) + if data then + return data + else + error("Failed to read shortcut \"" .. tostring(path) .. "\": " .. tostring(reason)) + end +end + +function system.call(method, ...) + local args = {...} + local function launchMethod() + method(table.unpack(args)) + end + + local function tracebackMethod(xpcallTraceback) + local traceback, info, firstMatch = tostring(xpcallTraceback) .. "\n" .. debug.traceback() + for runLevel = 0, math.huge do + info = debug.getinfo(runLevel) + if info then + if (info.what == "main" or info.what == "Lua") and info.source ~= "=machine" then + if firstMatch then + return { + path = info.source:sub(2, -1), + line = info.currentline, + traceback = traceback + } + else + firstMatch = true + end + end + else + error("Failed to get debug info for runlevel " .. runLevel) + end + end + end + + local xpcallSuccess, xpcallReason = xpcall(launchMethod, tracebackMethod) + if type(xpcallReason) == "string" or type(xpcallReason) == "nil" then + xpcallReason = { + path = paths.system.libraries .. "System.lua", + line = 1, + traceback = "system fatal error: " .. tostring(xpcallReason) + } + end + + if not xpcallSuccess and not xpcallReason.traceback:match("^table") and not xpcallReason.traceback:match("interrupted") then + return false, xpcallReason.path, xpcallReason.line, xpcallReason.traceback + end + + return true +end + +function system.setPackageUnloading(value) + local metatable = getmetatable(package.loaded) + + if value then + if metatable then + metatable.__mode = "v" + else + setmetatable(package.loaded, {__mode = "v"}) + end + else + if metatable then + metatable.__mode = nil + + for key in pairs(metatable) do + return + end + + setmetatable(package.loaded, nil) + end + end +end + +-------------------------------------------------------------------------------- + +local iconLaunchers = { + application = function(icon) + system.execute(icon.path .. "Main.lua") + end, + + directory = function(icon) + icon.parent.parent:setWorkpath(icon.path) + end, + + shortcut = function(icon) + local oldPath = icon.path + icon.path = icon.shortcutPath + icon:shortcutLaunch() + icon.path = oldPath + end, + + corrupted = function(icon) + GUI.alert("Application is corrupted") + end, + + extension = function(icon) + if icon.isShortcut then + system.execute(system.properties.extensions[icon.shortcutExtension].launcher, icon.shortcutPath, "-o") + else + system.execute(system.properties.extensions[icon.extension].launcher, icon.path, "-o") + end + end, + + script = function(icon) + system.execute(icon.path) + end, + + showPackageContent = function(icon) + icon.parent.parent:setWorkpath(icon.path) + icon.parent.parent:updateFileList() + + workspace:draw() + end, + + showContainingFolder = function(icon) + icon.parent.parent:setWorkpath(filesystem.path(icon.shortcutPath)) + icon.parent.parent:updateFileList() + + workspace:draw() + end, +} + +function system.calculateIconProperties() + iconHalfWidth = math.floor(system.properties.iconWidth / 2) + iconTextHeight = system.properties.iconHeight - iconImageHeight - 1 + iconImageHorizontalOffset = math.floor(iconHalfWidth - iconImageWidth / 2) +end + +function system.updateIconProperties() + desktopIconField:deleteIconConfig() + dockContainer.sort() + + computer.pushSignal("system", "updateFileList") +end + +function system.cacheIcon(name, path) + if not iconCache[name] then + iconCache[name] = image.load(path) + end + + return iconCache[name] +end + +local function drawSelection(x, y, width, height, color, transparency) + screen.drawText(x, y, color, string.rep("▄", width), transparency) + screen.drawText(x, y + height - 1, color, string.rep("▀", width), transparency) + screen.drawRectangle(x, y + 1, width, height - 2, color, 0x0, " ", transparency) +end + +local function iconDraw(icon) + local selectionTransparency = system.properties.interfaceTransparencyEnabled and 0.5 + local name = system.properties.filesShowExtension and icon.name or icon.nameWithoutExtension + local xCenter, yText = icon.x + iconHalfWidth, icon.y + iconImageHeight + 1 + + local function iconDrawNameLine(y, line) + local lineLength = unicode.len(line) + local x = math.floor(xCenter - lineLength / 2) + + if icon.selected then + screen.drawRectangle(x, y, lineLength, 1, icon.colors.selection, 0x0, " ", selectionTransparency) + end + screen.drawText(x, y, icon.colors.text, line) + end + + local charIndex = 1 + for lineIndex = 1, iconTextHeight do + if lineIndex < iconTextHeight then + iconDrawNameLine(yText, unicode.sub(name, charIndex, charIndex + icon.width - 1)) + charIndex, yText = charIndex + icon.width, yText + 1 + else + iconDrawNameLine(yText, text.limit(unicode.sub(name, charIndex, -1), icon.width, "center")) + end + end + + local xImage = icon.x + iconImageHorizontalOffset + if icon.selected then + drawSelection(xImage - 1, icon.y - 1, iconImageWidth + 2, iconImageHeight + 2, icon.colors.selection, selectionTransparency) + end + + if icon.image then + if icon.cut then + if not icon.semiTransparentImage then + icon.semiTransparentImage = image.copy(icon.image) + for i = 1, #icon.semiTransparentImage[3] do + icon.semiTransparentImage[5][i] = icon.semiTransparentImage[5][i] + 0.6 + if icon.semiTransparentImage[5][i] > 1 then + icon.semiTransparentImage[5][i] = 1 + end + end + end + + screen.drawImage(xImage, icon.y, icon.semiTransparentImage, true) + else + screen.drawImage(xImage, icon.y, icon.image) + end + elseif icon.liveImage then + icon.liveImage(xImage, icon.y) + end + + local xShortcut = xImage + iconImageWidth + if icon.isShortcut then + screen.set(xShortcut - 1, icon.y + iconImageHeight - 1, 0xFFFFFF, 0x0, "<") + end + + if icon.windows then + screen.drawText(xCenter - 1, icon.y + iconImageHeight, 0x66DBFF, "╺╸") + + if icon.windowCount > 1 then + local windowCount = tostring(icon.windowCount) + local windowCountLength = #windowCount + local xTip, yTip = xShortcut - windowCountLength, icon.y + + screen.drawRectangle(xTip, yTip, windowCountLength, 1, 0xFF4940, 0xFFFFFF, " ") + screen.drawText(xTip, yTip, 0xFFFFFF, windowCount) + screen.drawText(xTip - 1, yTip, 0xFF4940, "⢸") + screen.drawText(xTip + windowCountLength, yTip, 0xFF4940, "⡇") + screen.drawText(xTip, yTip - 1, 0xFF4940, string.rep("⣀", windowCountLength)) + screen.drawText(xTip, yTip + 1, 0xFF4940, string.rep("⠉", windowCountLength)) + end + end +end + +local function iconFieldSaveIconPosition(iconField, filename, x, y) + if iconField.iconConfigEnabled then + iconField.iconConfig[filename] = { x = x, y = y } + iconField:saveIconConfig() + end +end + +local function iconFieldIconEventHandler(workspace, object, e1, e2, e3, e4, e5, ...) + if e1 == "touch" and object:isPointInside(e3, e4) then + object.lastTouchPosition = object.lastTouchPosition or {} + object.lastTouchPosition.x, object.lastTouchPosition.y = e3, e4 + object:moveToFront() + + if e5 == 0 then + object.parent.parent.onLeftClick(object, e1, e2, e3, e4, e5, ...) + else + object.parent.parent.onRightClick(object, e1, e2, e3, e4, e5, ...) + end + elseif e1 == "double_touch" and object:isPointInside(e3, e4) and e5 == 0 then + object.parent.parent.onDoubleClick(object, e1, e2, e3, e4, e5, ...) + elseif e1 == "drag" and object.parent.parent.iconConfigEnabled and object.lastTouchPosition then + -- Ебучие авторы мода, ну на кой хуй было делать drop-ивент без наличия drag? ПИДОРЫ + object.dragStarted = true + object.localX = object.localX + e3 - object.lastTouchPosition.x + object.localY = object.localY + e4 - object.lastTouchPosition.y + object.lastTouchPosition.x, object.lastTouchPosition.y = e3, e4 + + workspace:draw() + elseif e1 == "drop" and object.dragStarted then + object.dragStarted = nil + object.lastTouchPosition = nil + + iconFieldSaveIconPosition( + object.parent.parent, + object.name .. (object.isDirectory and "/" or ""), + object.localX, + object.localY + ) + end +end + +local function iconAnalyseExtension(icon, launchers) + if icon.isDirectory then + if icon.extension == ".app" then + if system.properties.filesShowApplicationIcon then + if filesystem.exists(icon.path .. "Icon.pic") then + icon.image = image.load(icon.path .. "Icon.pic") + elseif filesystem.exists(icon.path .. "Icon.lua") then + local result, reason = loadfile(icon.path .. "Icon.lua") + if result then + result, reason = pcall(result) + if result then + icon.liveImage = reason + else + error("Failed to load live icon image: " .. tostring(reason)) + end + else + error("Failed to load live icon image: " .. tostring(reason)) + end + else + icon.image = iconCache.fileNotExists + end + else + icon.image = iconCache.application + end + + icon.launch = launchers.application + else + icon.image = iconCache.directory + icon.launch = launchers.directory + end + else + if icon.extension == ".lnk" then + icon.shortcutPath = system.readShortcut(icon.path) + icon.shortcutExtension = filesystem.extension(icon.shortcutPath) + icon.shortcutIsDirectory = filesystem.isDirectory(icon.shortcutPath) + icon.isShortcut = true + + local shortcutIcon = iconAnalyseExtension( + { + path = icon.shortcutPath, + extension = icon.shortcutExtension, + name = icon.name, + nameWithoutExtension = icon.nameWithoutExtension, + isDirectory = icon.shortcutIsDirectory, + iconImage = icon.iconImage, + }, + launchers + ) + + icon.image = shortcutIcon.image + icon.shortcutLaunch = shortcutIcon.launch + icon.launch = launchers.shortcut + elseif not filesystem.exists(icon.path) then + icon.image = iconCache.fileNotExists + icon.launch = launchers.corrupted + else + if system.properties.extensions[icon.extension] then + icon.launch = launchers.extension + icon.image = system.cacheIcon(icon.extension, system.properties.extensions[icon.extension].icon) + else + icon.launch = launchers.script + icon.image = iconCache.script + end + end + end + + return icon +end + +local function iconIsPointInside(icon, x, y) + return + x >= icon.x + iconImageHorizontalOffset and + y >= icon.y and + x <= icon.x + iconImageHorizontalOffset + iconImageWidth - 1 and + y <= icon.y + iconImageHeight - 1 + or + x >= icon.x and + y >= icon.y + iconImageHeight + 1 and + x <= icon.x + system.properties.iconWidth - 1 and + y <= icon.y + system.properties.iconHeight - 1 +end + +function system.icon(x, y, path, textColor, selectionColor) + local icon = GUI.object(x, y, system.properties.iconWidth, system.properties.iconHeight) + + icon.colors = { + text = textColor, + selection = selectionColor + } + + icon.path = path + icon.extension = filesystem.extension(icon.path) + icon.name = filesystem.name(path) + icon.nameWithoutExtension = filesystem.hideExtension(icon.name) + icon.isDirectory = filesystem.isDirectory(icon.path) + icon.isShortcut = false + icon.selected = false + + icon.isPointInside = iconIsPointInside + icon.draw = iconDraw + icon.analyseExtension = iconAnalyseExtension + + return icon +end + +local function iconFieldUpdate(iconField) + iconField.backgroundObject.width, iconField.backgroundObject.height = iconField.width, iconField.height + iconField.iconsContainer.width, iconField.iconsContainer.height = iconField.width, iconField.height + + iconField.iconCount.horizontal = math.floor((iconField.width - iconField.xOffset) / (system.properties.iconWidth + system.properties.iconHorizontalSpace)) + iconField.iconCount.vertical = math.floor((iconField.height - iconField.yOffset) / (system.properties.iconHeight + system.properties.iconVerticalSpace)) + iconField.iconCount.total = iconField.iconCount.horizontal * iconField.iconCount.vertical + + return iconField +end + +local function iconFieldLoadIconConfig(iconField) + local configPath = iconField.workpath .. ".icons" + if filesystem.exists(configPath) then + iconField.iconConfig = filesystem.readTable(configPath) + else + iconField.iconConfig = {} + end +end + +local function iconFieldSaveIconConfig(iconField) + filesystem.writeTable(iconField.workpath .. ".icons", iconField.iconConfig) +end + +local function iconFieldDeleteIconConfig(iconField) + iconField.iconConfig = {} + filesystem.remove(iconField.workpath .. ".icons", iconField.iconConfig) +end + +-------------------------------------------------------------------------------- + +local function addBackgroundContainerInput(parent, ...) + return parent:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x969696, 0xE1E1E1, 0x2D2D2D, ...)) +end + +local function addBackgroundContainerWithInput(inputText, title, placeholder) + local container = GUI.addBackgroundContainer(workspace, true, true, title) + + container.input = addBackgroundContainerInput(container.layout, inputText, placeholder, false) + container.label = container.layout:addChild(GUI.label(1, 1, 36, 1, 0xFF4940, system.localization.file .. " " .. system.localization.alreadyExists)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + container.label.hidden = true + + return container +end + +local function checkFileToExists(container, path) + if filesystem.exists(path) then + container.label.hidden = false + container.parent:draw() + else + container:remove() + return true + end +end + +local function getCykaIconPosition(iconField) + local y = iconField.yOffset + for i = 1, #iconField.iconsContainer.children do + y = math.max(y, iconField.iconsContainer.children[i].localY) + end + + local x = iconField.xOffset + for i = 1, #iconField.iconsContainer.children do + if iconField.iconsContainer.children[i].localY == y then + x = math.max(x, iconField.iconsContainer.children[i].localX) + end + end + + x = x + system.properties.iconWidth + system.properties.iconHorizontalSpace + if x + system.properties.iconWidth + system.properties.iconHorizontalSpace > iconField.iconsContainer.width then + x, y = iconField.xOffset, y + system.properties.iconHeight + system.properties.iconVerticalSpace + end + + return x, y +end + +local function iconOnLeftClick(icon) + if not keyboard.isKeyDown(29) and not keyboard.isKeyDown(219) then + icon.parent.parent:deselectAll() + end + icon.selected = true + + workspace:draw() +end + +local function iconOnDoubleClick(icon) + icon:launch() + icon.selected = false + + workspace:draw() +end + +local function iconOnRightClick(icon, e1, e2, e3, e4) + icon.selected = true + workspace:draw() + + local selectedIcons = icon.parent.parent:getSelectedIcons() + + local contextMenu = GUI.addContextMenu(workspace, e3, e4) + + contextMenu.onMenuClosed = function() + icon.parent.parent:deselectAll() + workspace:draw() + end + + if #selectedIcons == 1 then + if icon.isDirectory then + if icon.extension == ".app" then + contextMenu:addItem(system.localization.showPackageContent).onTouch = function() + icon.parent.parent.launchers.showPackageContent(icon) + end + + contextMenu:addItem(system.localization.launchWithArguments).onTouch = function() + local container = addBackgroundContainerWithInput("", system.localization.launchWithArguments) + + container.panel.eventHandler = function(workspace, object, e1) + if e1 == "touch" then + local args = {} + if container.input.text then + for arg in container.input.text:gmatch("[^%s]+") do + table.insert(args, arg) + end + end + + container:remove() + system.execute(icon.path .. "Main.lua", table.unpack(args)) + workspace:draw() + end + end + + workspace:draw() + end + + contextMenu:addItem(system.localization.edit .. " Main.lua").onTouch = function() + system.execute(paths.system.applicationMineCodeIDE, icon.path .. "Main.lua") + end + + contextMenu:addSeparator() + end + + if icon.extension ~= ".app" then + contextMenu:addItem(system.localization.addToFavourites).onTouch = function() + local container = GUI.addBackgroundContainer(workspace, true, true, system.localization.addToFavourites) + + local input = addBackgroundContainerInput(container.layout, icon.name, system.localization.name) + container.panel.eventHandler = function(workspace, object, e1) + if e1 == "touch" then + container:remove() + + if e1 == "touch" and #input.text > 0 then + computer.pushSignal("Finder", "updateFavourites", {name = input.text, path = icon.path}) + else + workspace:draw() + end + end + end + end + end + else + if icon.isShortcut then + contextMenu:addItem(system.localization.editShortcut).onTouch = function() + local text = system.readShortcut(icon.path) + local container = addBackgroundContainerWithInput(text, system.localization.editShortcut, system.localization.rename) + + container.input.onInputFinished = function() + if filesystem.exists(container.input.text) then + system.createShortcut(icon.path, container.input.text) + container:remove() + computer.pushSignal("system", "updateFileList") + else + container.label.text = system.localization.shortcutIsCorrupted + container.label.hidden = false + + workspace:draw() + end + end + + workspace:draw() + end + + contextMenu:addItem(system.localization.showContainingFolder).onTouch = function() + icon.parent.parent.launchers.showContainingFolder(icon) + end + + contextMenu:addSeparator() + else + if system.properties.extensions[icon.extension] and system.properties.extensions[icon.extension].contextMenu then + local success, reason = pcall(loadfile(system.properties.extensions[icon.extension].contextMenu), workspace, icon, contextMenu) + if success then + contextMenu:addSeparator() + else + GUI.alert("Failed to load extension association: " .. tostring(reason)) + end + else + contextMenu:addItem(system.localization.edit).onTouch = function() + system.execute(paths.system.applicationMineCodeIDE, icon.path) + end + + contextMenu:addSeparator() + end + + -- local subMenu = contextMenu:addSubMenu(system.localization.openWith) + -- local fileList = filesystem.sortedList(paths.system.applications, "name") + -- subMenu:addItem(system.localization.select) + -- subMenu:addSeparator() + -- for i = 1, #fileList do + -- subMenu:addItem(fileList[i].nameWithoutExtension) + -- end + end + end + end + + if #selectedIcons > 1 then + contextMenu:addItem(system.localization.newFolderFromChosen .. " (" .. #selectedIcons .. ")").onTouch = function() + local container = addBackgroundContainerWithInput("", system.localization.newFolderFromChosen .. " (" .. #selectedIcons .. ")", system.localization.folderName) + + container.input.onInputFinished = function() + local path = filesystem.path(selectedIcons[1].path) .. container.input.text + if checkFileToExists(container, path) then + filesystem.makeDirectory(path) + + for i = 1, #selectedIcons do + filesystem.rename(selectedIcons[i].path, path .. "/" .. selectedIcons[i].name) + end + + iconFieldSaveIconPosition(icon.parent.parent, container.input.text, e3, e4) + computer.pushSignal("system", "updateFileList") + end + end + + workspace:draw() + end + + contextMenu:addSeparator() + end + + contextMenu:addItem(system.localization.archive .. (#selectedIcons > 1 and " (" .. #selectedIcons .. ")" or "")).onTouch = function() + local itemsToArchive = {} + for i = 1, #selectedIcons do + table.insert(itemsToArchive, selectedIcons[i].path) + end + + local success, reason = require("Archive").pack(filesystem.path(icon.path) .. "/Archive.arc", itemsToArchive) + if not success then + GUI.alert(reason) + end + + computer.pushSignal("system", "updateFileList") + end + + local function cutOrCopy(cut) + for i = 1, #icon.parent.children do + icon.parent.children[i].cut = nil + end + + system.clipboard = {cut = cut} + for i = 1, #selectedIcons do + selectedIcons[i].cut = cut + table.insert(system.clipboard, selectedIcons[i].path) + end + end + + contextMenu:addItem(system.localization.cut).onTouch = function() + cutOrCopy(true) + end + + contextMenu:addItem(system.localization.copy).onTouch = function() + cutOrCopy() + end + + if not icon.isShortcut or #selectedIcons > 1 then + local subMenu = contextMenu:addSubMenu(system.localization.createShortcut) + + subMenu:addItem(system.localization.inCurrentDirectory).onTouch = function() + for i = 1, #selectedIcons do + if not selectedIcons[i].isShortcut then + system.createShortcut( + filesystem.path(selectedIcons[i].path) .. "/" .. selectedIcons[i].nameWithoutExtension .. ".lnk", + selectedIcons[i].path + ) + end + end + + computer.pushSignal("system", "updateFileList") + end + + subMenu:addItem(system.localization.onDesktop).onTouch = function() + for i = 1, #selectedIcons do + if not selectedIcons[i].isShortcut then + system.createShortcut( + paths.user.desktop .. "/" .. selectedIcons[i].nameWithoutExtension .. ".lnk", + selectedIcons[i].path + ) + end + end + + computer.pushSignal("system", "updateFileList") + end + end + + if #selectedIcons == 1 then + contextMenu:addItem(system.localization.rename).onTouch = function() + local container = addBackgroundContainerWithInput(filesystem.name(icon.path), system.localization.rename, system.localization.newName) + + container.input.onInputFinished = function() + if checkFileToExists(container, filesystem.path(icon.path) .. container.input.text) then + filesystem.rename(icon.path, filesystem.path(icon.path) .. container.input.text) + computer.pushSignal("system", "updateFileList") + end + end + + workspace:draw() + end + end + + contextMenu:addItem(system.localization.delete).onTouch = function() + for i = 1, #selectedIcons do + if filesystem.path(selectedIcons[i].path) == paths.user.trash then + filesystem.remove(selectedIcons[i].path) + else + local newName = paths.user.trash .. selectedIcons[i].name + local clearName = selectedIcons[i].nameWithoutExtension + local repeats = 1 + while filesystem.exists(newName) do + newName, repeats = paths.user.trash .. clearName .. string.rep("-copy", repeats) .. selectedIcons[i].extension, repeats + 1 + end + filesystem.rename(selectedIcons[i].path, newName) + end + end + + computer.pushSignal("system", "updateFileList") + end + + contextMenu:addSeparator() + + if #selectedIcons == 1 then + contextMenu:addItem(system.localization.addToDock).onTouch = function() + dockContainer.addIcon(icon.path).keepInDock = true + dockContainer.saveProperties() + end + end + + contextMenu:addItem(system.localization.properties).onTouch = function() + for i = 1, #selectedIcons do + system.addPropertiesWindow(e3, e4, 46, selectedIcons[i]) + end + end + + workspace:draw() +end + +local function iconFieldUpdateFileList(iconField) + local list, reason = filesystem.list(iconField.workpath, system.properties.filesSortingMethod) + if list then + iconField.fileList = list + + local i = 1 + while i <= #iconField.fileList do + if + ( + not system.properties.filesShowHidden and + filesystem.isHidden(iconField.fileList[i]) + ) + or + ( + iconField.filenameMatcher and + not unicode.lower(iconField.fileList[i]):match(iconField.filenameMatcher) + ) + then + table.remove(iconField.fileList, i) + else + i = i + 1 + end + end + + iconField:update() + + if iconField.iconConfigEnabled then + iconField:loadIconConfig() + end + + local configList, notConfigList = {}, {} + for i = iconField.fromFile, iconField.fromFile + iconField.iconCount.total - 1 do + if iconField.fileList[i] then + if iconField.iconConfigEnabled and iconField.iconConfig[iconField.fileList[i]] then + table.insert(configList, iconField.fileList[i]) + else + table.insert(notConfigList, iconField.fileList[i]) + end + else + break + end + end + + local function checkClipboard(icon) + if system.clipboard and system.clipboard.cut then + for i = 1, #system.clipboard do + if system.clipboard[i] == icon.path then + icon.cut = true + end + end + end + end + + -- Заполнение дочернего контейнера + iconField.iconsContainer:removeChildren() + for i = 1, #configList do + local icon = iconField.iconsContainer:addChild(system.icon( + iconField.iconConfig[configList[i]].x, + iconField.iconConfig[configList[i]].y, + iconField.workpath .. configList[i], + iconField.colors.text, + iconField.colors.selection + )) + + checkClipboard(icon) + icon.eventHandler = iconFieldIconEventHandler + icon:analyseExtension(iconField.launchers) + end + + local x, y + if #configList > 0 then + x, y = getCykaIconPosition(iconField, configList) + else + x, y = iconField.xOffset, iconField.yOffset + end + + for i = 1, #notConfigList do + local icon = iconField.iconsContainer:addChild(system.icon(x, y, iconField.workpath .. notConfigList[i], iconField.colors.text, iconField.colors.selection)) + iconField.iconConfig[notConfigList[i]] = {x = x, y = y} + + checkClipboard(icon) + icon.eventHandler = iconFieldIconEventHandler + icon:analyseExtension(iconField.launchers) + + x = x + system.properties.iconWidth + system.properties.iconHorizontalSpace + if x + system.properties.iconWidth + system.properties.iconHorizontalSpace - 1 > iconField.iconsContainer.width then + x, y = iconField.xOffset, y + system.properties.iconHeight + system.properties.iconVerticalSpace + end + end + + if iconField.iconConfigEnabled then + iconField:saveIconConfig() + end + else + GUI.alert("Failed to update file list: " .. tostring(reason)) + end + + return iconField +end + +local function iconFieldBackgroundObjectEventHandler(workspace, object, e1, e2, e3, e4, e5, ...) + if e1 == "touch" then + if e5 == 0 then + object.parent:deselectAll() + object.parent.selection = { + x1 = e3, + y1 = e4 + } + + workspace:draw() + else + local contextMenu = GUI.addContextMenu(workspace, e3, e4) + + local subMenu = contextMenu:addSubMenu(system.localization.create) + + subMenu:addItem(system.localization.newFile).onTouch = function() + local container = addBackgroundContainerWithInput("", system.localization.newFile, system.localization.fileName) + + container.input.onInputFinished = function() + local path = object.parent.workpath .. container.input.text + if checkFileToExists(container, path) then + filesystem.write(path, "") + + iconFieldSaveIconPosition(object.parent, container.input.text, e3, e4) + system.execute(paths.system.applicationMineCodeIDE, path) + computer.pushSignal("system", "updateFileList") + end + end + + workspace:draw() + end + + subMenu:addItem(system.localization.newFolder).onTouch = function() + local container = addBackgroundContainerWithInput("", system.localization.newFolder, system.localization.folderName) + + container.input.onInputFinished = function() + local path = object.parent.workpath .. container.input.text + if checkFileToExists(container, path) then + filesystem.makeDirectory(path) + iconFieldSaveIconPosition(object.parent, container.input.text .. "/", e4, e4) + computer.pushSignal("system", "updateFileList") + end + end + + workspace:draw() + end + + subMenu:addItem(system.localization.newImage).onTouch = function() + local container = addBackgroundContainerWithInput("", system.localization.newImage, system.localization.fileName) + + local layout = container.layout:addChild(GUI.layout(1, 1, 36, 3, 1, 1)) + layout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL) + layout:setSpacing(1, 1, 0) + + local widthInput = addBackgroundContainerInput(layout, "", "width") + layout:addChild(GUI.text(1, 1, 0x696969, " x ")) + local heightInput = addBackgroundContainerInput(layout, "", "height") + widthInput.width, heightInput.width = 16, 17 + + container.panel.eventHandler = function(workspace, panel, e1) + if e1 == "touch" then + if + #container.input.text > 0 and + widthInput.text:match("%d+") and + heightInput.text:match("%d+") + then + local imageName = filesystem.hideExtension(container.input.text) .. ".pic" + local path = object.parent.workpath .. imageName + + if checkFileToExists(container, path) then + image.save(path, image.create( + tonumber(widthInput.text), + tonumber(heightInput.text), + 0x0, + 0xFFFFFF, + 1, + " " + )) + + iconFieldSaveIconPosition(object.parent, imageName, e3, e4) + computer.pushSignal("system", "updateFileList") + end + end + + container:remove() + workspace:draw() + end + end + + workspace:draw() + end + + subMenu:addItem(system.localization.newFileFromURL, not component.isAvailable("internet")).onTouch = function() + local container = addBackgroundContainerWithInput("", system.localization.newFileFromURL, system.localization.fileName) + + local inputURL = addBackgroundContainerInput(container.layout, "", "URL", false) + + container.panel.eventHandler = function(workspace, panel, e1) + if e1 == "touch" then + if #container.input.text > 0 and #inputURL.text > 0 then + local path = object.parent.workpath .. container.input.text + + if filesystem.exists(path) then + container.label.hidden = false + workspace:draw() + else + container.layout:removeChildren(2) + container.layout:addChild(GUI.label(1, 1, container.width, 1, 0x787878, system.localization.downloading .. "...")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + workspace:draw() + + local success, reason = require("Internet").download(inputURL.text, path) + + container:remove() + + if success then + iconFieldSaveIconPosition(object.parent, container.input.text, e3, e4) + computer.pushSignal("system", "updateFileList") + else + GUI.alert(reason) + workspace:draw() + end + end + else + container:remove() + workspace:draw() + end + end + end + + workspace:draw() + end + + subMenu:addSeparator() + + subMenu:addItem(system.localization.newApplication).onTouch = function() + local container = addBackgroundContainerWithInput("", system.localization.newApplication, system.localization.applicationName) + + local filesystemChooser = container.layout:addChild(GUI.filesystemChooser(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x444444, 0x969696, nil, system.localization.open, system.localization.cancel, system.localization.iconPath, "/")) + filesystemChooser:addExtensionFilter(".pic") + filesystemChooser:moveBackward() + + container.panel.eventHandler = function(workspace, panel, e1) + if e1 == "touch" then + if #container.input.text > 0 then + local path = object.parent.workpath .. container.input.text .. ".app/" + if checkFileToExists(container, path) then + system.copy({ paths.system.applicationSample }, object.parent.workpath) + filesystem.rename(object.parent.workpath .. filesystem.name(paths.system.applicationSample), path) + + container:remove() + iconFieldSaveIconPosition(object.parent, container.input.text .. ".app/", e3, e4) + computer.pushSignal("system", "updateFileList") + end + else + container:remove() + workspace:draw() + end + end + end + + workspace:draw() + end + + contextMenu:addSeparator() + + local subMenu = contextMenu:addSubMenu(system.localization.sortBy) + + local function setSortingMethod(sm) + object.parent:deleteIconConfig() + system.properties.filesSortingMethod = sm + system.saveProperties() + + computer.pushSignal("system", "updateFileList") + end + + subMenu:addItem(system.localization.sortByName).onTouch = function() + setSortingMethod(filesystem.SORTING_NAME) + end + + subMenu:addItem(system.localization.sortByDate).onTouch = function() + setSortingMethod(filesystem.SORTING_DATE) + end + + subMenu:addItem(system.localization.sortByType).onTouch = function() + setSortingMethod(filesystem.SORTING_TYPE) + end + + contextMenu:addItem(system.localization.sortAutomatically).onTouch = function() + object.parent:deleteIconConfig() + computer.pushSignal("system", "updateFileList") + end + + contextMenu:addItem(system.localization.update).onTouch = function() + computer.pushSignal("system", "updateFileList") + end + + contextMenu:addSeparator() + + contextMenu:addItem(system.localization.paste, not system.clipboard).onTouch = function() + local i = 1 + while i <= #system.clipboard do + if filesystem.exists(system.clipboard[i]) then + i = i + 1 + else + table.remove(system.clipboard, i) + end + end + + system.copy(system.clipboard, object.parent.workpath) + + if system.clipboard.cut then + for i = 1, #system.clipboard do + filesystem.remove(system.clipboard[i]) + end + + system.clipboard = nil + end + + computer.pushSignal("system", "updateFileList") + end + + workspace:draw() + end + elseif e1 == "drag" then + if object.parent.selection then + object.parent.selection.x2, object.parent.selection.y2 = e3, e4 + object:moveToFront() + + workspace:draw() + end + elseif e1 == "drop" then + object.parent.selection = nil + object:moveToBack() + + workspace:draw() + end +end + +local function iconFieldBackgroundObjectDraw(object) + if object.parent.selection and object.parent.selection.x2 then + local x1, y1, x2, y2 = object.parent.selection.x1, object.parent.selection.y1, object.parent.selection.x2, object.parent.selection.y2 + + if x2 < x1 then + x1, x2 = x2, x1 + end + + if y2 < y1 then + y1, y2 = y2, y1 + end + + if system.properties.interfaceTransparencyEnabled then + screen.drawRectangle(x1, y1, x2 - x1 + 1, y2 - y1 + 1, object.parent.colors.selection, 0x0, " ", 0.5) + else + screen.drawFrame(x1, y1, x2 - x1 + 1, y2 - y1 + 1, object.parent.colors.selection) + end + + for i = 1, #object.parent.iconsContainer.children do + local xCenter, yCenter = object.parent.iconsContainer.children[i].x + system.properties.iconWidth / 2, object.parent.iconsContainer.children[i].y + system.properties.iconHeight / 2 + object.parent.iconsContainer.children[i].selected = + xCenter >= x1 and + xCenter <= x2 and + yCenter >= y1 and + yCenter <= y2 + end + end +end + +local function iconFieldDeselectAll(iconField) + for i = 1, #iconField.iconsContainer.children do + iconField.iconsContainer.children[i].selected = false + end +end + +local function iconFieldGetSelectedIcons(iconField) + local selectedIcons = {} + + for i = 1, #iconField.iconsContainer.children do + if iconField.iconsContainer.children[i].selected then + table.insert(selectedIcons, iconField.iconsContainer.children[i]) + end + end + + return selectedIcons +end + +local function iconFieldSetWorkpath(iconField, path) + iconField.workpath = path + iconField.filenameMatcher = nil + iconField.fromFile = 1 + + return iconField +end + +function system.iconField(x, y, width, height, xOffset, yOffset, textColor, selectionColor, workpath) + local iconField = GUI.container(x, y, width, height) + + iconField.colors = { + text = textColor, + selection = selectionColor + } + + iconField.iconConfig = {} + iconField.iconCount = {} + iconField.fileList = {} + iconField.fromFile = 1 + iconField.iconConfigEnabled = false + iconField.xOffset = xOffset + iconField.yOffset = yOffset + iconField.workpath = workpath + iconField.filenameMatcher = nil + + iconField.backgroundObject = iconField:addChild(GUI.object(1, 1, width, height)) + iconField.backgroundObject.eventHandler = iconFieldBackgroundObjectEventHandler + iconField.backgroundObject.draw = iconFieldBackgroundObjectDraw + + iconField.iconsContainer = iconField:addChild(GUI.container(1, 1, width, height)) + + iconField.updateFileList = iconFieldUpdateFileList + iconField.update = iconFieldUpdate + iconField.deselectAll = iconFieldDeselectAll + iconField.loadIconConfig = iconFieldLoadIconConfig + iconField.saveIconConfig = iconFieldSaveIconConfig + iconField.deleteIconConfig = iconFieldDeleteIconConfig + iconField.getSelectedIcons = iconFieldGetSelectedIcons + iconField.setWorkpath = iconFieldSetWorkpath + + iconField.onLeftClick = iconOnLeftClick + iconField.onRightClick = iconOnRightClick + iconField.onDoubleClick = iconOnDoubleClick + + -- Duplicate icon launchers for overriding possibility + iconField.launchers = {} + for key, value in pairs(iconLaunchers) do + iconField.launchers[key] = value + end + + return iconField +end + +-------------------------------------------------------------------------------- + +local function updateMenu() + local focusedWindow = windowsContainer.children[#windowsContainer.children] + desktopMenu.children = focusedWindow and focusedWindow.menu.children or system.menuInitialChildren +end + +local function windowRemove(window) + if window.dockIcon then + -- Удаляем ссылку на окно из докиконки + window.dockIcon.windows[window] = nil + window.dockIcon.windowCount = window.dockIcon.windowCount - 1 + + -- Если в докиконке еще остались окна + if not next(window.dockIcon.windows) then + window.dockIcon.windows = nil + window.dockIcon.windowCount = nil + + if not window.dockIcon.keepInDock then + window.dockIcon:remove() + dockContainer.sort() + end + end + end + + -- Удаляем само окошко + table.remove(window.parent.children, window:indexOf()) + updateMenu() +end + +function system.addWindow(window, dontAddToDock, preserveCoordinates) + -- Чекаем коорды + if not preserveCoordinates then + window.x, window.y = math.floor(windowsContainer.width / 2 - window.width / 2), math.floor(windowsContainer.height / 2 - window.height / 2) + end + + -- Ебурим окно к окнам + windowsContainer:addChild(window) + + if not dontAddToDock then + -- Получаем путь залупы + local info + for i = 0, math.huge do + info = debug.getinfo(i) + if info then + if info.source and info.what == "main" and info.source:sub(-13, -1) == ".app/Main.lua" then + local dockPath = filesystem.removeSlashes(info.source:sub(2, -9)) + + -- Чекаем наличие иконки в доке с таким же путем, и еси ее нет, то хуячим новую + for i = 1, #dockContainer.children do + if dockContainer.children[i].path == dockPath then + window.dockIcon = dockContainer.children[i] + break + end + end + + if not window.dockIcon then + window.dockIcon = dockContainer.addIcon(dockPath) + end + + -- Ебурим ссылку на окна в иконку + window.dockIcon.windows = window.dockIcon.windows or {} + window.dockIcon.windows[window] = true + window.dockIcon.windowCount = (window.dockIcon.windowCount or 0) + 1 + + -- Взалупливаем иконке индивидуальную менюху. По дефолту тут всякая хуйня и прочее + window.menu = GUI.menu(1, 1, 1) + window.menu.colors = desktopMenu.colors + local name = filesystem.hideExtension(filesystem.name(dockPath)) + local contextMenu = window.menu:addContextMenu(name, 0x0) + + contextMenu:addItem(system.localization.closeWindow .. " " .. name, false, "^W").onTouch = function() + window:remove() + end + + -- Смещаем окно правее и ниже, если уже есть открытые окна этой софтины + local lastIndex + for i = #windowsContainer.children, 1, -1 do + if windowsContainer.children[i] ~= window and window.dockIcon.windows[windowsContainer.children[i]] then + lastIndex = i + break + end + end + + if lastIndex then + window.localX, window.localY = windowsContainer.children[lastIndex].localX + 4, windowsContainer.children[lastIndex].localY + 2 + end + + -- Когда окно фокусицца, то главная ОСевая менюха заполницца ДЕТИШЕЧКАМИ оконной менюхи + window.onFocus = updateMenu + + -- Заполняем главную менюху текущим окном + updateMenu() + + break + end + else + break + end + end + end + + -- "Закрытие" акошычка + window.remove = windowRemove + + -- Кнопочкам тоже эту хуйню пихаем + if window.actionButtons then + window.actionButtons.close.onTouch = function() + window:remove() + end + + window.actionButtons.maximize.onTouch = function() + window:maximize() + end + + window.actionButtons.minimize.onTouch = function() + window:minimize() + end + end + + return workspace, window, window.menu +end + +-------------------------------------------------------------------------------- + +function system.addPropertiesWindow(x, y, width, icon) + local workspace, window = system.addWindow(GUI.titledWindow(x, y, width, 1, system.localization.properties), true, true) + + window.backgroundPanel.colors.transparency = 0.2 + window:addChild(GUI.image(2, 3, icon.image)) + + local x, y = 11, 3 + + local function addKeyAndValue(key, value) + local object = window:addChild(GUI.keyAndValue(x, y, 0x3C3C3C, 0x5A5A5A, key, ": " .. value)) + y = y + 1 + + return object + end + + addKeyAndValue(system.localization.type, icon.extension or (icon.isDirectory and system.localization.folder or system.localization.unknown)) + local sizeKeyAndValue = addKeyAndValue(system.localization.size, icon.isDirectory and "-" or string.format("%.2f", filesystem.size(icon.path) / 1024) .. " KB") + addKeyAndValue(system.localization.date, os.date("%d.%m.%y, %H:%M", math.floor(filesystem.lastModified(icon.path) / 1000))) + addKeyAndValue(system.localization.path, " ") + + local textBox = window:addChild(GUI.textBox(17, y - 1, window.width - 18, 1, nil, 0x555555, {icon.path}, 1, 0, 0, true, true)) + textBox.eventHandler = nil + + window.actionButtons.minimize:remove() + window.actionButtons.maximize:remove() + + window.height = textBox.y + textBox.height + window.backgroundPanel.width = window.width + window.backgroundPanel.height = textBox.y + textBox.height + + workspace:draw() +end + +----------------------------------------------------------------------------------------------------------------------------------- + +function system.copy(fileList, toPath) + local applyYes, breakRecursion + + local container = GUI.addBackgroundContainer(workspace, true, true, system.localization.copying) + local textBox = container.layout:addChild(GUI.textBox(1, 1, container.width, 1, nil, 0x787878, {}, 1, 0, 0, true, true):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)) + local switchAndLabel = container.layout:addChild(GUI.switchAndLabel(1, 1, 37, 8, 0x66DB80, 0x1E1E1E, 0xE1E1E1, 0x787878, system.localization.applyToAll .. ":", false)) + container.panel.eventHandler = nil + + local buttonsLayout = container.layout:addChild(GUI.layout(1, 1, 1, 1, 1, 1)) + buttonsLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL) + buttonsLayout:setSpacing(1, 1, 2) + + buttonsLayout:addChild(GUI.button(1, 1, 11, 1, 0xE1E1E1, 0x2D2D2D, 0xA5A5A5, 0x2D2D2D, system.localization.yes)).onTouch = function() + applyYes = true + workspace:stop() + end + buttonsLayout:addChild(GUI.button(1, 1, 11, 1, 0xE1E1E1, 0x2D2D2D, 0xA5A5A5, 0x2D2D2D, system.localization.no)).onTouch = function() + workspace:stop() + end + buttonsLayout:addChild(GUI.button(1, 1, 11, 1, 0xE1E1E1, 0x2D2D2D, 0xA5A5A5, 0x2D2D2D, system.localization.cancel)).onTouch = function() + breakRecursion = true + workspace:stop() + end + + buttonsLayout:fitToChildrenSize(1, 1) + + local function copyOrMove(path, finalPath) + switchAndLabel.hidden = true + buttonsLayout.hidden = true + + textBox.lines = { + system.localization.copying .. " " .. system.localization.faylaBlyad .. " " .. filesystem.name(path) .. " " .. system.localization.toDirectory .. " " .. filesystem.removeSlashes(toPath), + } + textBox:update() + + workspace:draw() + + filesystem.remove(finalPath) + + filesystem.copy(path, finalPath) + end + + local function recursiveCopy(path, toPath) + local finalPath = toPath .. "/" .. filesystem.name(path) + + if filesystem.isDirectory(path) then + filesystem.makeDirectory(finalPath) + + local list = filesystem.list(path) + for i = 1, #list do + if breakRecursion then + return + end + recursiveCopy(path .. "/" .. list[i], finalPath) + end + else + if filesystem.exists(finalPath) then + if not switchAndLabel.switch.state then + switchAndLabel.hidden = false + buttonsLayout.hidden = false + applyYes = false + + textBox.lines = { + system.localization.file .. " " .. filesystem.name(path) .. " " .. system.localization.alreadyExists .. " " .. system.localization.inDirectory .. " " .. filesystem.removeSlashes(toPath), + system.localization.needReplace, + } + textBox:update() + + workspace:draw() + workspace:start() + workspace:draw() + end + + if applyYes then + copyOrMove(path, finalPath) + end + else + copyOrMove(path, finalPath) + end + end + end + + for i = 1, #fileList do + recursiveCopy(fileList[i], toPath) + end + + container:remove() + workspace:draw() +end + +local function menuWidgetEventHandler(workspace, object, e1, ...) + if e1 == "touch" and object.onTouch then + object.selected = true + workspace:draw() + + object.onTouch(workspace, object, e1, ...) + + object.selected = false + workspace:draw() + end +end + +local function menuWidgetDraw(object) + if object.selected then + object.textColor = 0xFFFFFF + screen.drawRectangle(object.x - 1, object.y, object.width + 2, 1, 0x3366CC, object.textColor, " ") + else + object.textColor = 0x0 + end + + object.drawContent(object) +end + +function system.menuWidget(width) + local object = GUI.object(1, 1, width, 1) + + object.selected = false + object.eventHandler = menuWidgetEventHandler + object.draw = menuWidgetDraw + + return object +end + +function system.addMenuWidget(object) + desktopMenuLayout:addChild(object) + object:moveToBack() + + return object +end + +-------------------------------------------------------------------------------- + +function system.error(path, line, traceback) + local container = GUI.addBackgroundContainer(workspace, true, false, false) + + local window = container:addChild(GUI.container(1, 1, screen.getWidth(), math.floor(container.height * 0.5))) + window.localY = math.floor(container.height / 2 - window.height / 2) + + window:addChild(GUI.panel(1, 1, window.width, 3, 0x3C3C3C)) + window:addChild(GUI.label(1, 2, window.width, 1, 0xE1E1E1, system.localization.errorWhileRunningProgram .. "\"" .. filesystem.name(path) .. "\"")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + local actionButtons = window:addChild(GUI.actionButtons(2, 2, true)) + local sendToDeveloperButton = window:addChild(GUI.adaptiveButton(9, 1, 2, 1, 0x4B4B4B, 0xD2D2D2, 0x2D2D2D, 0xFFFFFF, system.localization.sendFeedback)) + + local codeView = window:addChild(GUI.codeView(1, 4, math.floor(window.width * 0.62), window.height - 3, 1, 1, 100, {}, {[line] = 0xFF4444}, GUI.LUA_SYNTAX_PATTERNS, GUI.LUA_SYNTAX_COLOR_SCHEME, true, {})) + + -- Obtain from- and to- lines that need to be shown + codeView.fromLine = line - math.floor((window.height - 3) / 2) + 1 + if codeView.fromLine <= 0 then + codeView.fromLine = 1 + end + local toLine, lineCounter = codeView.fromLine + codeView.height - 1, 1 + + -- Read part of file to display error line + for line in filesystem.lines(path) do + if lineCounter >= codeView.fromLine and lineCounter <= toLine then + codeView.lines[lineCounter] = line:gsub("\t", " ") + elseif lineCounter < codeView.fromLine then + codeView.lines[lineCounter] = " " + elseif lineCounter > toLine then + break + end + + lineCounter = lineCounter + 1 + + if lineCounter % 200 == 0 then + computer.pullSignal(0) + end + end + + -- Stacktrace parsing + local lines = text.wrap(traceback:gsub("\r\n", "\n"):gsub("\t", " "), window.width - codeView.width - 2) + window:addChild(GUI.textBox(codeView.width + 1, 4, window.width - codeView.width, codeView.height, 0xFFFFFF, 0x0, lines, 1, 1, 0)) + + actionButtons.close.onTouch = function() + container:remove() + workspace:draw() + end + + window.eventHandler = function(workspace, object, e1, e2, e3, e4) + if e1 == "key_down" and e4 == 28 then + actionButtons.close.onTouch() + end + end + + sendToDeveloperButton.onTouch = function() + if component.isAvailable("internet") then + local internet = require("Internet") + internet.request("https://api.mcmodder.ru/ECS/report.php?path=" .. internet.encode(path) .. "&errorMessage=" .. internet.encode(traceback)) + + sendToDeveloperButton.text = system.localization.sendedFeedback + workspace:draw() + event.sleep(1) + end + + actionButtons.close.onTouch() + end + + workspace:draw() + + for i = 1, 3 do + component.get("computer").beep(1500, 0.08) + end +end + +function system.execute(path, ...) + path = filesystem.removeSlashes(path) + + local oldScreenWidth, oldScreenHeight, success, errorPath, line, traceback = screen.getResolution() + + if filesystem.exists(path) then + success, reason = loadfile(path) + + if success then + success, errorPath, line, traceback = system.call(success, ...) + else + success, errorPath, line, traceback = false, path, tonumber(reason:match(":(%d+)%:")) or 1, reason + end + else + GUI.alert("File \"" .. tostring(path) .. "\" doesn't exists") + end + + component.proxy(screen.getGPUProxy().getScreen()).setPrecise(false) + screen.setResolution(oldScreenWidth, oldScreenHeight) + + if not success then + system.error(errorPath, line, traceback) + end + + return success, errorPath, line, traceback +end + +---- Хуй +local function updateWallpaper(path, mode, brightness) + local result, reason = image.load(path) + if result then + desktopBackgroundWallpaper = result + + -- Fit to screen size mode + if mode == 1 then + desktopBackgroundWallpaper = image.transform(desktopBackgroundWallpaper, workspace.width, workspace.height) + desktopBackgroundWallpaperX, desktopBackgroundWallpaperY = 1, 1 + -- Centerized mode + else + desktopBackgroundWallpaperX = math.floor(1 + workspace.width / 2 - image.getWidth(desktopBackgroundWallpaper) / 2) + desktopBackgroundWallpaperY = math.floor(1 + workspace.height / 2 - image.getHeight(desktopBackgroundWallpaper) / 2) + end + + -- Brightness adjustment + local background, foreground, alpha, symbol, r, g, b + for y = 1, image.getHeight(desktopBackgroundWallpaper) do + for x = 1, image.getWidth(desktopBackgroundWallpaper) do + background, foreground, alpha, symbol = image.get(desktopBackgroundWallpaper, x, y) + + r, g, b = color.integerToRGB(background) + background = color.RGBToInteger( + math.floor(r * brightness), + math.floor(g * brightness), + math.floor(b * brightness) + ) + + r, g, b = color.integerToRGB(foreground) + foreground = color.RGBToInteger( + math.floor(r * brightness), + math.floor(g * brightness), + math.floor(b * brightness) + ) + + image.set(desktopBackgroundWallpaper, x, y, background, foreground, alpha, symbol) + end + end + else + GUI.alert("Failed to load wallpaper: " .. (reason or "image file is corrupted")) + end +end + +function system.updateWallpaper() + desktopBackgroundWallpaper = nil + + if system.properties.interfaceWallpaperEnabled and system.properties.interfaceWallpaperPath then + updateWallpaper( + system.properties.interfaceWallpaperPath, + system.properties.interfaceWallpaperMode, + system.properties.interfaceWallpaperBrightness + ) + end +end + +function system.updateResolution() + if system.properties.interfaceScreenWidth then + screen.setResolution(system.properties.interfaceScreenWidth, system.properties.interfaceScreenHeight) + else + screen.setResolution(screen.getGPUProxy().maxResolution()) + end + + workspace.width, workspace.height = screen.getResolution() + + desktopIconField.width = workspace.width + desktopIconField.height = workspace.height + desktopIconField:updateFileList() + + dockContainer.sort() + dockContainer.localY = workspace.height - dockContainer.height + 1 + + desktopMenu.width = workspace.width + desktopMenuLayout.width = workspace.width + desktopBackground.width, desktopBackground.height = workspace.width, workspace.height + + windowsContainer.width, windowsContainer.height = workspace.width, workspace.height - 1 +end + +local function moveDockIcon(index, direction) + dockContainer.children[index], dockContainer.children[index + direction] = dockContainer.children[index + direction], dockContainer.children[index] + dockContainer.sort() + dockContainer.saveProperties() + workspace:draw() +end + +local function getPercentageColor(pecent) + if pecent >= 0.75 then + return 0x00B640 + elseif pecent >= 0.6 then + return 0x99DB40 + elseif pecent >= 0.3 then + return 0xFFB640 + elseif pecent >= 0.2 then + return 0xFF9240 + else + return 0xFF4940 + end +end + +local function dockIconEventHandler(workspace, icon, e1, e2, e3, e4, e5, e6, ...) + if e1 == "touch" then + icon.selected = true + workspace:draw() + + if e5 == 1 then + icon.onRightClick(icon, e1, e2, e3, e4, e5, e6, ...) + else + icon.onLeftClick(icon, e1, e2, e3, e4, e5, e6, ...) + end + end +end + +function system.updateDesktop() + desktopIconField = workspace:addChild( + system.iconField( + 1, 2, 1, 1, 3, 2, + 0xFFFFFF, + 0xD2D2D2, + paths.user.desktop + ) + ) + + desktopIconField.iconConfigEnabled = true + + desktopIconField.launchers.directory = function(icon) + system.execute(paths.system.applicationFinder, "-o", icon.path) + end + + desktopIconField.launchers.showContainingFolder = function(icon) + system.execute(paths.system.applicationFinder, "-o", filesystem.path(icon.shortcutPath or icon.path)) + end + + desktopIconField.launchers.showPackageContent = function(icon) + system.execute(paths.system.applicationFinder, "-o", icon.path) + end + + dockContainer = workspace:addChild(GUI.container(1, 1, workspace.width, 7)) + + dockContainer.saveProperties = function() + system.properties.dockShortcuts = {} + for i = 1, #dockContainer.children do + if dockContainer.children[i].keepInDock then + table.insert(system.properties.dockShortcuts, dockContainer.children[i].path) + end + end + + system.saveProperties() + end + + dockContainer.sort = function() + local x = 4 + for i = 1, #dockContainer.children do + dockContainer.children[i].localX = x + x = x + system.properties.iconWidth + system.properties.iconHorizontalSpace + end + + dockContainer.width = #dockContainer.children * (system.properties.iconWidth + system.properties.iconHorizontalSpace) - system.properties.iconHorizontalSpace + 6 + dockContainer.localX = math.floor(workspace.width / 2 - dockContainer.width / 2) + end + + dockContainer.addIcon = function(path) + local icon = dockContainer:addChild(system.icon(1, 2, path, 0x2D2D2D, 0xFFFFFF)) + icon:analyseExtension(iconLaunchers) + icon:moveBackward() + + icon.eventHandler = dockIconEventHandler + + icon.onLeftClick = function(icon, ...) + if icon.windows then + for window in pairs(icon.windows) do + window.hidden = false + window:moveToFront() + end + + event.sleep(0.2) + + icon.selected = false + updateMenu() + workspace:draw() + else + iconOnDoubleClick(icon, ...) + end + end + + icon.onRightClick = function(icon, e1, e2, e3, e4, ...) + local indexOf = icon:indexOf() + local contextMenu = GUI.addContextMenu(workspace, e3, e4) + + contextMenu.onMenuClosed = function() + icon.selected = false + workspace:draw() + end + + if icon.windows then + local eventData = {...} + + contextMenu:addItem(system.localization.newWindow).onTouch = function() + iconOnDoubleClick(icon, e1, e2, e3, e4, table.unpack(eventData)) + end + + contextMenu:addItem(system.localization.closeAllWindows).onTouch = function() + for window in pairs(icon.windows) do + window:remove() + end + + workspace:draw() + end + end + + contextMenu:addItem(system.localization.showContainingFolder).onTouch = function() + system.execute(paths.system.applicationFinder, "-o", filesystem.path(icon.shortcutPath or icon.path)) + end + + contextMenu:addSeparator() + + contextMenu:addItem(system.localization.moveRight, indexOf >= #dockContainer.children - 1).onTouch = function() + moveDockIcon(indexOf, 1) + end + + contextMenu:addItem(system.localization.moveLeft, indexOf <= 1).onTouch = function() + moveDockIcon(indexOf, -1) + end + + contextMenu:addSeparator() + + if icon.keepInDock then + if #dockContainer.children > 1 then + contextMenu:addItem(system.localization.removeFromDock).onTouch = function() + if icon.windows then + icon.keepInDock = nil + else + icon:remove() + dockContainer.sort() + end + + workspace:draw() + dockContainer.saveProperties() + end + end + else + if icon.windows then + contextMenu:addItem(system.localization.keepInDock).onTouch = function() + icon.keepInDock = true + dockContainer.saveProperties() + end + end + end + + workspace:draw() + end + + dockContainer.sort() + + return icon + end + + -- Trash + local icon = dockContainer.addIcon(paths.user.trash) + icon.image = image.load(paths.system.icons .. "Trash.pic") + icon.launch = function() + system.execute(paths.system.applicationFinder, "-o", icon.path) + end + + icon.eventHandler = dockIconEventHandler + + icon.onLeftClick = iconOnDoubleClick + + icon.onRightClick = function(icon, e1, e2, e3, e4) + local contextMenu = GUI.addContextMenu(workspace, e3, e4) + + contextMenu.onMenuClosed = function() + icon.selected = false + workspace:draw() + end + + contextMenu:addItem(system.localization.emptyTrash).onTouch = function() + local container = GUI.addBackgroundContainer(workspace, true, true, system.localization.areYouSure) + + container.layout:addChild(GUI.button(1, 1, 30, 1, 0xE1E1E1, 0x2D2D2D, 0xA5A5A5, 0x2D2D2D, "OK")).onTouch = function() + local list = filesystem.list(paths.user.trash) + for i = 1, #list do + filesystem.remove(paths.user.trash .. list[i]) + end + container:remove() + computer.pushSignal("system", "updateFileList") + end + + container.panel.onTouch = function() + container:remove() + workspace:draw() + end + + workspace:draw() + end + + workspace:draw() + end + + for i = 1, #system.properties.dockShortcuts do + dockContainer.addIcon(system.properties.dockShortcuts[i]).keepInDock = true + end + + -- Draw dock drawDock dockDraw cyka заебался искать, блядь + local overrideDockContainerDraw = dockContainer.draw + dockContainer.draw = function(dockContainer) + local color, currentDockTransparency, currentDockWidth, xPos = system.properties.interfaceColorDock, system.properties.interfaceTransparencyDock, dockContainer.width - 2, dockContainer.x + + for y = dockContainer.y + dockContainer.height - 1, dockContainer.y + dockContainer.height - 4, -1 do + screen.drawText(xPos, y, color, "◢", system.properties.interfaceTransparencyEnabled and currentDockTransparency) + screen.drawRectangle(xPos + 1, y, currentDockWidth, 1, color, 0xFFFFFF, " ", system.properties.interfaceTransparencyEnabled and currentDockTransparency) + screen.drawText(xPos + currentDockWidth + 1, y, color, "◣", system.properties.interfaceTransparencyEnabled and currentDockTransparency) + + currentDockTransparency, currentDockWidth, xPos = currentDockTransparency + 0.08, currentDockWidth - 2, xPos + 1 + if currentDockTransparency > 1 then + currentDockTransparency = 1 + end + end + + overrideDockContainerDraw(dockContainer) + end + + windowsContainer = workspace:addChild(GUI.container(1, 2, 1, 1)) + + desktopMenu = workspace:addChild(GUI.menu(1, 1, workspace.width, 0x0, 0x696969, 0x3366CC, 0xFFFFFF)) + + local MineOSContextMenu = desktopMenu:addContextMenu("MineOS", 0x000000) + MineOSContextMenu:addItem(system.localization.aboutSystem).onTouch = function() + local container = GUI.addBackgroundContainer(workspace, true, true, system.localization.aboutSystem) + container.layout:removeChildren() + + local lines = { + "MineOS", + "Copyright © 2014-" .. os.date("%Y", system.getTime()), + " ", + "Developers:", + " ", + "Igor Timofeev, vk.com/id7799889", + "Gleb Trifonov, vk.com/id88323331", + "Yakov Verevkin, vk.com/id60991376", + "Alexey Smirnov, vk.com/id23897419", + "Timofey Shestakov, vk.com/id113499693", + " ", + "UX-advisers:", + " ", + "Nikita Yarichev, vk.com/id65873873", + "Vyacheslav Sazonov, vk.com/id21321257", + "Michail Prosin, vk.com/id75667079", + "Dmitrii Tiunov, vk.com/id151541414", + "Egor Paliev, vk.com/id83795932", + "Maxim Pakin, vk.com/id100687922", + "Andrey Kakoito, vk.com/id201043162", + "Maxim Omelaenko, vk.com/id54662296", + "Konstantin Mayakovskiy, vk.com/id10069748", + "Ruslan Isaev, vk.com/id181265169", + "Eugene8388608, vk.com/id287247631", + " ", + "Translators:", + " ", + "06Games, github.com/06Games", + "Xenia Mazneva, vk.com/id5564402", + "Yana Dmitrieva, vk.com/id155326634", + } + + local textBox = container.layout:addChild(GUI.textBox(1, 1, container.layout.width, #lines, nil, 0xB4B4B4, lines, 1, 0, 0)) + textBox:setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) + textBox.eventHandler = container.panel.eventHandler + + workspace:draw() + end + + MineOSContextMenu:addItem(system.localization.updates).onTouch = function() + system.execute(paths.system.applicationAppMarket, "updates") + end + + MineOSContextMenu:addSeparator() + + MineOSContextMenu:addItem(system.localization.logout).onTouch = function() + system.authorize() + end + + MineOSContextMenu:addItem(system.localization.reboot).onTouch = function() + require("Network").broadcastComputerState(false) + computer.shutdown(true) + end + + MineOSContextMenu:addItem(system.localization.shutdown).onTouch = function() + require("Network").broadcastComputerState(false) + computer.shutdown() + end + + desktopMenuLayout = workspace:addChild(GUI.layout(1, 1, 1, 1, 1, 1)) + desktopMenuLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL) + desktopMenuLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_RIGHT, GUI.ALIGNMENT_VERTICAL_TOP) + desktopMenuLayout:setMargin(1, 1, 1, 0) + desktopMenuLayout:setSpacing(1, 1, 2) + + local dateWidget, dateWidgetText = system.addMenuWidget(system.menuWidget(1)) + dateWidget.drawContent = function() + screen.drawText(dateWidget.x, 1, dateWidget.textColor, dateWidgetText) + end + + local batteryWidget, batteryWidgetPercent, batteryWidgetText = system.addMenuWidget(system.menuWidget(1)) + batteryWidget.drawContent = function() + screen.drawText(batteryWidget.x, 1, batteryWidget.textColor, batteryWidgetText) + + local pixelPercent = math.floor(batteryWidgetPercent * 4 + 0.5) + if pixelPercent == 0 then + pixelPercent = 1 + end + + local index = screen.getIndex(batteryWidget.x + #batteryWidgetText, 1) + for i = 1, 4 do + screen.rawSet(index, screen.rawGet(index), i <= pixelPercent and getPercentageColor(batteryWidgetPercent) or 0xD2D2D2, i < 4 and "⠶" or "╸") + index = index + 1 + end + end + + local RAMWidget, RAMPercent = system.addMenuWidget(system.menuWidget(16)) + RAMWidget.drawContent = function() + local text = "RAM: " .. math.ceil(RAMPercent * 100) .. "% " + local barWidth = RAMWidget.width - #text + local activeWidth = math.ceil(RAMPercent * barWidth) + + screen.drawText(RAMWidget.x, 1, RAMWidget.textColor, text) + + local index = screen.getIndex(RAMWidget.x + #text, 1) + for i = 1, barWidth do + screen.rawSet(index, screen.rawGet(index), i <= activeWidth and getPercentageColor(1 - RAMPercent) or 0xD2D2D2, "━") + index = index + 1 + end + end + + function system.updateMenuWidgets() + dateWidgetText = os.date(system.properties.timeFormat, system.properties.timeRealTimestamp and system.getTime() or nil) + dateWidget.width = unicode.len(dateWidgetText) + + batteryWidgetPercent = computer.energy() / computer.maxEnergy() + if batteryWidgetPercent == math.huge then + batteryWidgetPercent = 1 + end + batteryWidgetText = math.ceil(batteryWidgetPercent * 100) .. "% " + batteryWidget.width = #batteryWidgetText + 4 + + local totalMemory = computer.totalMemory() + RAMPercent = (totalMemory - computer.freeMemory()) / totalMemory + end + + local lastWindowHandled + workspace.eventHandler = function(workspace, object, e1, e2, e3, e4) + if e1 == "key_down" then + local windowCount = #windowsContainer.children + -- Ctrl or CMD + if windowCount > 0 and not lastWindowHandled and (keyboard.isKeyDown(29) or keyboard.isKeyDown(219)) then + -- W + if e4 == 17 then + windowsContainer.children[windowCount]:remove() + lastWindowHandled = true + + workspace:draw() + end + end + elseif lastWindowHandled and e1 == "key_up" and (e4 == 17 or e4 == 35) then + lastWindowHandled = false + elseif e1 == "system" then + if e2 == "updateFileList" then + desktopIconField:updateFileList() + workspace:draw() + end + elseif e1 == "network" then + if e2 == "accessDenied" then + GUI.alert(system.localization.networkAccessDenied) + elseif e2 == "timeout" then + GUI.alert(system.localization.networkTimeout) + end + end + + if computer.uptime() - dateUptime >= 1 then + system.updateMenuWidgets() + workspace:draw() + + dateUptime = computer.uptime() + end + + if system.properties.interfaceScreensaverEnabled then + if e1 then + screensaverUptime = computer.uptime() + end + + if dateUptime - screensaverUptime >= system.properties.interfaceScreensaverDelay then + if filesystem.exists(system.properties.interfaceScreensaverPath) then + system.execute(system.properties.interfaceScreensaverPath) + workspace:draw(true) + end + + screensaverUptime = computer.uptime() + end + end + end + + system.menuInitialChildren = desktopMenu.children + + system.updateResolution() + system.updateWallpaper() + system.updateMenuWidgets() +end + +function system.updateColorScheme() + -- Drop down menus + GUI.CONTEXT_MENU_BACKGROUND_TRANSPARENCY = system.properties.interfaceTransparencyEnabled and 0.18 + GUI.CONTEXT_MENU_SHADOW_TRANSPARENCY = system.properties.interfaceTransparencyEnabled and 0.4 + -- Windows + GUI.WINDOW_SHADOW_TRANSPARENCY = system.properties.interfaceTransparencyEnabled and 0.6 + -- Background containers + GUI.BACKGROUND_CONTAINER_PANEL_COLOR = system.properties.interfaceTransparencyEnabled and 0x0 or system.properties.interfaceColorDesktopBackground + GUI.BACKGROUND_CONTAINER_PANEL_TRANSPARENCY = system.properties.interfaceTransparencyEnabled and 0.3 + -- Top menu + desktopMenu.colors.default.background = system.properties.interfaceColorMenu + -- Desktop background + desktopBackgroundColor = system.properties.interfaceColorDesktopBackground +end + +-------------------------------------------------------------------------------- + +-- Runs tasks before/after OS UI initialization +local function runTasks(mode) + for i = 1, #system.properties.tasks do + local task = system.properties.tasks[i] + if task.mode == mode and task.enabled then + system.execute(task.path) + end + end +end + +function system.setUser(u) + user = u + + -- Updating paths + paths.updateUser(u) + + -- Updating current desktop iconField path to new one + if desktopIconField then + desktopIconField.workpath = paths.user.desktop + end +end + +local function updateUser(u) + system.setUser(u) + -- Loading localization + system.localization = system.getLocalization(paths.system.localizations) + -- Tasks before UI initialization + runTasks(2) + -- Recalculating icon internal sizes based on user properties + system.calculateIconProperties() + -- Creating desktop widgets + system.updateDesktop() + -- Updating color scheme + system.updateColorScheme() + -- Meowing + workspace:draw() + require("Network").update() + + -- Tasks after UI initialization + runTasks(1) +end + +local function userObjectDraw(userObject) + local center = userObject.x + userObject.width / 2 + local imageWidth = image.getWidth(userObject.icon) + local imageX = math.floor(center - imageWidth / 2) + + if userObject.selected then + drawSelection(imageX - 1, userObject.y - 1, imageWidth + 2, image.getHeight(userObject.icon) + 2, 0xFFFFFF, 0.5) + end + + screen.drawImage(imageX, userObject.y, userObject.icon) + screen.drawText(math.floor(center - unicode.len(userObject.name) / 2), userObject.y + image.getHeight(userObject.icon) + 1, 0xE1E1E1, userObject.name) +end + +local function userObjectEventHandler(workspace, userObject, e1) + if e1 == "touch" then + userObject.selected = true + workspace:draw() + + event.sleep(0.2) + + userObject.selected = false + userObject.onTouch() + end +end + +local function newUserObject(name, addEventHandler) + local userObject = GUI.object(1, 1, math.max(8, unicode.len(name)), 6) + + userObject.name = name + + local iconPath = paths.system.users .. name .. "/Icon.pic" + userObject.icon = image.load(filesystem.exists(iconPath) and iconPath or paths.system.icons .. "User.pic") + + userObject.draw = userObjectDraw + userObject.eventHandler = addEventHandler and userObjectEventHandler + + return userObject +end + +function system.updateWorkspace() + -- Clearing workspace + workspace:removeChildren() + + -- Creating background object + desktopBackgroundWallpaperX, desktopBackgroundWallpaperY = 1, 1 + desktopBackground = workspace:addChild(GUI.object(1, 1, workspace.width, workspace.height)) + desktopBackground.draw = function() + screen.drawRectangle(1, 1, desktopBackground.width, desktopBackground.height, desktopBackgroundColor, 0, " ") + + if desktopBackgroundWallpaper then + screen.drawImage(desktopBackgroundWallpaperX, desktopBackgroundWallpaperY, desktopBackgroundWallpaper) + end + end +end + +function system.getDefaultProperties() + return { + localizationLanguage = "English", + + timeFormat = "%d %b %Y %H:%M:%S", + timeRealTimestamp = true, + timeTimezone = 0, + + networkUsers = {}, + networkName = "Computer #" .. string.format("%06X", math.random(0x0, 0xFFFFFF)), + networkEnabled = true, + networkSignalStrength = 512, + networkFTPConnections = {}, + + interfaceWallpaperEnabled = false, + interfaceWallpaperPath = paths.system.pictures .. "Space.pic", + interfaceWallpaperMode = 2, + interfaceWallpaperBrightness = 0.9, + + interfaceScreensaverEnabled = false, + interfaceScreensaverPath = paths.system.screensavers .. "Matrix.lua", + interfaceScreensaverDelay = 20, + + interfaceTransparencyEnabled = true, + interfaceTransparencyDock = 0.4, + interfaceTransparencyMenu = 0.2, + interfaceTransparencyContextMenu = 0.2, + + interfaceColorDesktopBackground = 0x1E1E1E, + interfaceColorDock = 0xE1E1E1, + interfaceColorMenu = 0xFFFFFF, + + filesShowExtension = false, + filesShowHidden = false, + filesShowApplicationIcon = true, + + iconWidth = 12, + iconHeight = 6, + iconHorizontalSpace = 1, + iconVerticalSpace = 1, + + tasks = {}, + dockShortcuts = { + filesystem.path(paths.system.applicationAppMarket), + filesystem.path(paths.system.applicationMineCodeIDE), + filesystem.path(paths.system.applicationFinder), + filesystem.path(paths.system.applicationPictureEdit), + filesystem.path(paths.system.applicationSettings), + }, + extensions = { + [".3dm"] = { + icon = paths.system.icons .. "3DModel.pic", + launcher = paths.system.applications .. "3D Print.app/Main.lua", + contextMenu = paths.system.extensions .. "3dm/ContextMenu.lua" + }, + [".pic"] = { + icon = paths.system.icons .. "Image.pic", + launcher = paths.system.applicationPictureEdit, + contextMenu = paths.system.extensions .. "Pic/ContextMenu.lua" + }, + [".cfg"] = { + icon = paths.system.icons .. "Config.pic" + }, + [".txt"] = { + icon = paths.system.icons .. "Text.pic" + }, + [".arc"] = { + icon = paths.system.icons .. "Archive.pic", + launcher = paths.system.extensions .. "Arc/Launcher.lua" + }, + [".lua"] = { + icon = paths.system.icons .. "Lua.pic", + launcher = paths.system.extensions .. "Lua/Launcher.lua", + contextMenu = paths.system.extensions .. "Lua/ContextMenu.lua" + }, + ["script"] = { + icon = paths.system.icons .. "Script.pic", + launcher = paths.system.extensions .. "Lua/Launcher.lua", + contextMenu = paths.system.extensions .. "Lua/ContextMenu.lua" + }, + }, + } +end + +function system.createUser(name, language, password, wallpaper, screensaver) + -- Generating default user properties + local properties = system.getDefaultProperties() + + -- Injecting preferred properties + properties.localizationLanguage = language + properties.securityPassword = password and require("SHA-256").hash(password) + properties.interfaceWallpaperEnabled = wallpaper + properties.interfaceScreensaverEnabled = screensaver + + -- Generating user home directory tree + local userPaths = paths.getUser(name) + paths.create(userPaths) + + -- Creating basic user icon + filesystem.copy(paths.system.icons .. "User.pic", userPaths.home .. "Icon.pic") + + -- Saving user properties + filesystem.writeTable(userPaths.properties, properties, true) + + return properties, userPaths +end + +function system.setWorkspace(w) + workspace = w +end + +function system.getWorkspace() + return workspace +end + +function system.authorize() + system.updateWorkspace() + + -- Obtaining user list and removing non-directory files from it + local userList = filesystem.list(paths.system.users) + local i = 1 + while i <= #userList do + if filesystem.isDirectory(paths.system.users .. userList[i]) then + i = i + 1 + else + table.remove(userList, i) + end + end + + local function loadPropertiesAndCheckProtection(userName) + paths.updateUser(userName) + + if filesystem.exists(paths.user.properties) then + system.properties = filesystem.readTable(paths.user.properties) + + for key, value in pairs(system.getDefaultProperties()) do + if system.properties[key] == nil then + system.properties[key] = value + end + end + else + system.properties = system.getDefaultProperties() + end + + return system.properties.securityPassword + end + + -- Creating login UI only if there's more than one user with protection + if #userList > 1 or loadPropertiesAndCheckProtection(userList[1]) then + local container = workspace:addChild(GUI.container(1, 1, workspace.width, workspace.height)) + + -- Trying to load first wallpaper from system pictures directory + -- if not desktopBackgroundWallpaper then + -- local pictureList = filesystem.list(paths.system.pictures) + -- if pictureList then + -- for i = 1, #pictureList do + -- if filesystem.extension(pictureList[i]) == ".pic" then + -- updateWallpaper(paths.system.pictures .. pictureList[i], 1, 1) + -- break + -- end + -- end + -- end + -- end + + -- If we've loaded wallpaper (from user logout or above) then add a panel to make it darker + if desktopBackgroundWallpaper then + container:addChild(GUI.panel(1, 1, container.width, container.height, 0x0, 0.5)) + end + + local function addLayout(y, height, spacing) + local layout = container:addChild(GUI.layout(1, y, container.width, height, 1, 1)) + + layout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL) + layout:setSpacing(1, 1, spacing) + + return layout + end + + local buttonsLayout = addLayout(container.height - 2, 1, 4) + + local function addButton(text, onTouch) + buttonsLayout:addChild(GUI.adaptiveRoundedButton(1, 1, 1, 0, 0x4B4B4B, 0xE1E1E1, 0x787878, 0xE1E1E1, text)).onTouch = onTouch + end + + addButton("Reboot", function() + computer.shutdown(true) + end) + + addButton("Shutdown", function() + computer.shutdown() + end) + + local function selectUser() + local function securityCheck(userName) + local passwordLayout = container:addChild(GUI.layout(1, 1, container.width, container.height, 1, 1)) + + passwordLayout:addChild(newUserObject(userName)) + + local passwordContainer = passwordLayout:addChild(GUI.container(1, 1, 36, 3)) + local input = addBackgroundContainerInput(passwordContainer, "", "Password", false, "•") + + -- Adding "select user again" button only if there's more than 1 user + if #userList > 1 then + local backButton = passwordContainer:addChild(GUI.button(1, 1, 5, 3, 0x2D2D2D, 0xE1E1E1, 0x4B4B4B, 0xE1E1E1, "<")) + input.localX = backButton.width + 1 + input.width = input.width - backButton.width + + backButton.onTouch = function() + passwordLayout:remove() + selectUser() + end + end + + input.onInputFinished = function() + if #input.text > 0 then + local hash = require("SHA-256").hash(input.text) + + package.loaded["SHA-256"] = nil + input.text = "" + + if hash == system.properties.securityPassword then + container:remove() + updateUser(userName) + else + GUI.alert("Incorrect password") + end + end + end + + workspace:draw() + end + + if #userList > 1 then + local usersLayout = addLayout(1, container.height, 4) + + for i = 1, #userList do + local userObject = usersLayout:addChild(newUserObject(userList[i]:sub(1, -2), true)) + userObject.onTouch = function() + usersLayout:remove() + + -- Again, if there's some protection + if loadPropertiesAndCheckProtection(userObject.name) then + securityCheck(userObject.name) + else + container:remove() + updateUser(userObject.name) + end + end + end + else + securityCheck(userList[1]:sub(1, -2)) + end + end + + selectUser() + else + updateUser(userList[1]) + end + + workspace:draw() +end + +-------------------------------------------------------------------------------- + +-- Optaining temporary file's last modified UNIX timestamp as boot timestamp +local temporaryPath = system.getTemporaryPath() +filesystem.write(temporaryPath, "") +bootRealTime = math.floor(filesystem.lastModified(temporaryPath) / 1000) +filesystem.remove(temporaryPath) + +-- Caching commonly used icons +system.cacheIcon("directory", paths.system.icons .. "Folder.pic") +system.cacheIcon("fileNotExists", paths.system.icons .. "FileNotExists.pic") +system.cacheIcon("application", paths.system.icons .. "Application.pic") +system.cacheIcon("script", paths.system.icons .. "Script.pic") + +-------------------------------------------------------------------------------- + +return system diff --git a/Libraries/Text.lua b/Libraries/Text.lua new file mode 100755 index 00000000..ccfcd88f --- /dev/null +++ b/Libraries/Text.lua @@ -0,0 +1,232 @@ + +local text = {} + +-------------------------------------------------------------------------------- + +function text.serialize(t, prettyLook, indentationWidth, indentUsingTabs, recursionStackLimit) + checkArg(1, t, "table") + + recursionStackLimit = recursionStackLimit or math.huge + local indentationSymbolAdder = string.rep(indentUsingTabs and " " or " ", indentationWidth or 2) + local equalsSymbol = prettyLook and " = " or "=" + + local function serialize(t, currentIndentationSymbol, currentRecusrionStack) + local result, nextIndentationSymbol, keyType, valueType, stringValue = {"{"}, currentIndentationSymbol .. indentationSymbolAdder + + if prettyLook then + table.insert(result, "\n") + end + + for key, value in pairs(t) do + keyType, valueType, stringValue = type(key), type(value), tostring(value) + + if prettyLook then + table.insert(result, nextIndentationSymbol) + end + + if keyType == "number" then + table.insert(result, "[") + table.insert(result, key) + table.insert(result, "]") + table.insert(result, equalsSymbol) + elseif keyType == "string" then + -- Короч, если типа начинается с буковки, а также если это алфавитно-нумерическая поеботня + if prettyLook and key:match("^%a") and key:match("^[%w%_]+$") then + table.insert(result, key) + else + table.insert(result, "[\"") + table.insert(result, key) + table.insert(result, "\"]") + end + + table.insert(result, equalsSymbol) + end + + if valueType == "number" or valueType == "boolean" or valueType == "nil" then + table.insert(result, stringValue) + elseif valueType == "string" or valueType == "function" then + table.insert(result, "\"") + table.insert(result, stringValue) + table.insert(result, "\"") + elseif valueType == "table" then + if currentRecusrionStack < recursionStackLimit then + table.insert( + result, + table.concat( + serialize( + value, + nextIndentationSymbol, + currentRecusrionStack + 1 + ) + ) + ) + else + table.insert(result, "\"…\"") + end + end + + table.insert(result, ",") + + if prettyLook then + table.insert(result, "\n") + end + end + + -- Удаляем запятую + if prettyLook then + if #result > 2 then + table.remove(result, #result - 1) + end + + table.insert(result, currentIndentationSymbol) + else + if #result > 1 then + table.remove(result, #result) + end + end + + table.insert(result, "}") + + return result + end + + return table.concat(serialize(t, "", 1)) +end + +function text.deserialize(s) + checkArg(1, s, "string") + + local result, reason = load("return " .. s) + if result then + result, reason = pcall(result) + + if result then + return reason + else + return nil, reason + end + else + return nil, reason + end +end + +function text.split(s, delimiter) + local parts, index = {}, 1 + for part in s:gmatch(delimiter) do + parts[index] = part + index = index + 1 + end + + return parts +end + +function text.brailleChar(a, b, c, d, e, f, g, h) + return unicode.char(10240 + 128 * h + 64 * g + 32 * f + 16 * d + 8 * b + 4 * e + 2 * c + a) +end + +function text.unicodeFind(s, pattern, init, plain) + if init then + if init < 0 then + init = -#unicode.sub(s, init) + elseif init > 0 then + init = #unicode.sub(s, 1, init - 1) + 1 + end + end + + a, b = s:find(pattern, init, plain) + + if a then + local ap, bp = s:sub(1, a - 1), s:sub(a,b) + a = unicode.len(ap) + 1 + b = a + unicode.len(bp) - 1 + + return a, b + else + return a + end +end + +function text.limit(s, limit, mode, noDots) + local length = unicode.len(s) + + if length <= limit then + return s + elseif mode == "left" then + if noDots then + return unicode.sub(s, length - limit + 1, -1) + else + return "…" .. unicode.sub(s, length - limit + 2, -1) + end + elseif mode == "center" then + local integer, fractional = math.modf(limit / 2) + if fractional == 0 then + return unicode.sub(s, 1, integer) .. "…" .. unicode.sub(s, -integer + 1, -1) + else + return unicode.sub(s, 1, integer) .. "…" .. unicode.sub(s, -integer, -1) + end + else + if noDots then + return unicode.sub(s, 1, limit) + else + return unicode.sub(s, 1, limit - 1) .. "…" + end + end +end + +function text.wrap(data, limit) + if type(data) == "string" then + data = { data } + end + + local wrappedLines, result, preResult, position = {} + + -- Дублируем таблицу строк, шоб не перекосоебить ченить переносами + for i = 1, #data do + wrappedLines[i] = data[i] + end + + -- Отсечение возврата каретки-ебуретки + local i = 1 + while i <= #wrappedLines do + local position = wrappedLines[i]:find("\n") + if position then + table.insert(wrappedLines, i + 1, unicode.sub(wrappedLines[i], position + 1, -1)) + wrappedLines[i] = unicode.sub(wrappedLines[i], 1, position - 1) + end + + i = i + 1 + end + + -- Сам перенос + local i = 1 + while i <= #wrappedLines do + result = "" + + for word in wrappedLines[i]:gmatch("[^%s]+") do + preResult = result .. word + + if unicode.len(preResult) > limit then + if unicode.len(word) > limit then + table.insert(wrappedLines, i + 1, unicode.sub(wrappedLines[i], limit + 1, -1)) + result = unicode.sub(wrappedLines[i], 1, limit) + else + table.insert(wrappedLines, i + 1, unicode.sub(wrappedLines[i], unicode.len(result) + 1, -1)) + end + + break + else + result = preResult .. " " + end + end + + wrappedLines[i] = result:gsub("%s+$", ""):gsub("^%s+", "") + + i = i + 1 + end + + return wrappedLines +end + +-------------------------------------------------------------------------------- + +return text \ No newline at end of file diff --git a/lib/vector.lua b/Libraries/Vector.lua similarity index 100% rename from lib/vector.lua rename to Libraries/Vector.lua diff --git a/Localizations/OS/English.lang b/Localizations/English.lang similarity index 52% rename from Localizations/OS/English.lang rename to Localizations/English.lang index 7c6309b3..e9ef1c11 100755 --- a/Localizations/OS/English.lang +++ b/Localizations/English.lang @@ -1,15 +1,24 @@ { - iconPath = "Icon path", downloading = "Downloading in progress", + CPUArchitecture = "CPU architecture", + RAMControl = "RAM control", + packageUnloading = "Automatic unloading", networkFTPNewConnection = "FTP-connection", networkFTPConnections = "FTP-connections", networkFTPAddress = "Server address", networkFTPPort = "Server port", networkFTPUser = "User", - networkFTPPasswprd = "Password", + networkFTPPassword = "Password", + updatingFileList = "Updating file list...", network = "Network", networkTimeout = "Remote computer is not responding", networkAccessDenied = "Access to the filesystem of the remote computer is denied. Contact it's owner to get appropriate privileges", + networkAllowReadAndWrite = "Filesystem access", + networkModemNotAvailable = "Attach modem to use network functions", + networkState = "Enable network mode", + networkComputers = "Network PCs options", + networkName = "Name of this PC", + networkSearchRadius = "Searching radius", openWith = "Open with", select = "Choose application…", keepInDock = "Keep in dock", @@ -38,6 +47,9 @@ resolution = "Screen resolution", pressAnyKeyToContinue = "Press any key to continue", + screensaver = "Screensaver", + screensaverDelay = "Delay", + screensaverEnabled = "Screensaver enabled", areYouSure = "Are you sure?", emptyTrash = "Empty trash", @@ -55,11 +67,13 @@ newFolder = "Folder", newFile = "File", newFileFromURL = "File from URL", + newImage = "Image", newApplication = "MineOS application", paste = "Paste", copy = "Copy", cut = "Cut", edit = "Edit", + editInPhotoshop = "Edit in Photoshop", rename = "Rename", editShortcut = "Edit shortcut", createShortcut = "Create shortcut", @@ -81,19 +95,71 @@ sortByType = "By type", sortByName = "By name", sortByDate = "By date", + showExtension = "File extensions", + showHiddenFiles = "Hidden files", + showApplicationIcons = "Application icons", aboutSystem = "About system", updates = "Updates", update = "Refresh", shutdown = "Shutdown", + logout = "Logout", reboot = "Reboot", returnToShell = "Return to Shell", - + + protectYourComputer = "Computer protection", + inputPassword = "Input password", + confirmInputPassword = "Confirm password", + oldPassword = "Old password", + newPassword = "New password", + setProtectionMethod = "Change computer protection method", + wrongOldPassword = "Wrong old password!", + passwordSucessfullyChanged = "Password has been successfully changed!", + withoutProtection = "Without protection", + passwordProtection = "Password protection", + biometricProtection = "Biometric protection", + putFingerToVerify = "Put your finger to authorize", + putFingerToRegister = "Put your finger to create a biometric signature", + fingerprintCreated = "Biometric signature has been created", + accessDenied = "Access denied", + welcomeBack = "Welcome, ", + passwordsAreDifferent = "Passwords are different", + incorrectPassword = "Incorrect password", + mineOSCreatorUsedMasterPassword = "The creator of this operating system has used Master-Password", + loginToSystem = "Login", + + systemLanguage = "System language", + colorScheme = "Color scheme", + iconProperties = "Icons properties", + spaceBetweenIcons = "Space bewteen icons", + sizeOfIcons = "Size of icons", + byHorizontal = "Horizontal", + byVertical = "Vertical", + wallpaper = "Wallpaper", + wallpaperPath = "Path to wallpaper", + iconPath = "Path to icon", + wallpaperModeStretch = "Stretch", + wallpaperModeCenter = "Center", + wallpaperEnabled = "Wallpaper enabled", + wallpaperBrightness = "Wallpaper brightness", + wallpaperSwitchInfo = "Disabling this option will decrease RAM usage and increase system perfomance", + backgroundColor = "Background color", + menuColor = "Menu color", + dockColor = "Dock color", + transparencyEnabled = "Transparency enabled", + transparencySwitchInfo = "Disabling this option will increase system perfomance", + screenResolution = "Screen resolution", + changePassword = "Change password", shortcutIsCorrupted = "Shortcut is linked to non-existent file", sortAutomatically = "Align to grid", onDesktop = "On desktop", inCurrentDirectory = "In current directory", + settings = "Preferences", + dateAndTime = "Date and time", + timezone = "Timezone", + dateFormat = "Date format", + errorWhileRunningProgram = "Error while running ", sendedFeedback = "Feedback was sent", sendFeedback = "Send feedback", diff --git a/Localizations/OS/French.lang b/Localizations/French.lang similarity index 51% rename from Localizations/OS/French.lang rename to Localizations/French.lang index fc59e646..2bf484ad 100644 --- a/Localizations/OS/French.lang +++ b/Localizations/French.lang @@ -1,15 +1,24 @@ { - iconPath = "Chemin d'icône", downloading = "Téléchargement en cours", + CPUArchitecture = "Architecture du processeur", + RAMControl = "Contrôle de la RAM", + packageUnloading = "Déchargement automatique", networkFTPNewConnection = "Connexion FTP", networkFTPConnections = "Connexions FTP", networkFTPAddress = "Adresse du serveur", networkFTPPort = "Port du serveur", networkFTPUser = "Utilisateur", - networkFTPPasswprd = "Mot de passe", + networkFTPPassword = "Mot de passe", + updatingFileList = "Mise à jour de la liste des fichiers ...", network = "Réseau", networkTimeout = "L'ordinateur distant ne répond pas", networkAccessDenied = "L'accès au système de fichiers de l'ordinateur distant est refusé. Contactez son propriétaire pour obtenir les privilèges appropriés", + networkAllowReadAndWrite = "Accès au système de fichiers", + networkModemNotAvailable = "Joindre un modem pour utiliser les fonctions réseau", + networkState = "Activer le mode réseau", + networkComputers = "Options du réseau de PC", + networkName = "Nom de ce PC", + networkSearchRadius = "Rayon de recherche", openWith = "Ouvrir avec", select = "Choisissez l'application ...", keepInDock = "Gardez dans la barre des tâches", @@ -38,6 +47,9 @@ resolution = "Résolution d'écran", pressAnyKeyToContinue = "Appuyez sur n'importe quelle touche pour continuer", + screensaver = "Économiseur d'écran", + screensaverDelay = "Délai", + screensaverEnabled = "Économiseur d'écran activé", areYouSure = "Êtes-vous sûr ?", emptyTrash = "Corbeille vide", @@ -55,11 +67,13 @@ newFolder = "Dossier", newFile = "Fichier", newFileFromURL = "Fichier à partir d'une URL", + newImage = "Image", newApplication = "Application MineOS", paste = "Coller", copy = "Copier", cut = "Couper", edit = "Éditer", + editInPhotoshop = "Modifier dans Photoshop", rename = "Renommer", editShortcut = "Modifier le raccourci", createShortcut = "Créer un raccourci", @@ -81,19 +95,71 @@ sortByType = "Par type", sortByName = "Par nom", sortByDate = "Par date", + showExtension = "Extensions de fichier", + showHiddenFiles = "Fichiers cachés", + showApplicationIcons = "Icônes d'application", aboutSystem = "À propos du système", updates = "Mises à jour", update = "Rafraîchir", shutdown = "Éteindre", + logout = "Se déconnecter", reboot = "Redémarrer", returnToShell = "Retourner à l'invite de commandes", - + + protectYourComputer = "Protection de l'ordinateur", + inputPassword = "Saisir le mot de passe", + confirmInputPassword = "Confirmez le mot de passe", + oldPassword = "Ancien mot de passe", + newPassword = "Nouveau mot de passe", + setProtectionMethod = "Changer la méthode de protection de l'ordinateur", + wrongOldPassword = "Ancien mot de passe érroné !", + passwordSucessfullyChanged = "Le mot de passe a été changé avec succès !", + withoutProtection = "Sans protection", + passwordProtection = "Protection par mot de passe", + biometricProtection = "Protection biométrique", + putFingerToVerify = "Mettez votre doigt pour déverrouiller", + putFingerToRegister = "Mettez votre doigt pour créer une signature biométrique", + fingerprintCreated = "La signature biométrique a été créée", + accessDenied = "Accès refusé", + welcomeBack = "Bienvenue, ", + passwordsAreDifferent = "Les mots de passe sont différents", + incorrectPassword = "Mot de passe incorrect", + mineOSCreatorUsedMasterPassword = "Le créateur de ce système d'exploitation a utilisé un mot de passe avec des droits root", + loginToSystem = "S'identifier", + + systemLanguage = "Langue du système", + colorScheme = "Couleurs principales", + iconProperties = "Propriétés des icônes", + spaceBetweenIcons = "Espacement entre les icônes", + sizeOfIcons = "Taille des icônes", + byHorizontal = "Horizontal", + byVertical = "Verticale", + wallpaper = "Fond d'écran", + wallpaperPath = "Chemin d'accès du fond d'écran", + iconPath = "Chemin d'accès de l'icône", + wallpaperModeStretch = "Étiré", + wallpaperModeCenter = "Centré", + wallpaperEnabled = "Fond d'écran activé", + wallpaperBrightness = "Luminosité du fond d'écran", + wallpaperSwitchInfo = "La désactivation de cette option réduit l'utilisation de la RAM et augmente les performances du système", + backgroundColor = "Couleur de fond", + menuColor = "Couleur du menu", + dockColor = "Couleur de la barre des tâches", + transparencyEnabled = "Transparence activée", + transparencySwitchInfo = "La désactivation de cette option augmentera les performances du système", + screenResolution = "Résolution d'écran", + changePassword = "Changer le mot de passe", shortcutIsCorrupted = "Le raccourci est lié à un fichier inexistant", sortAutomatically = "Aligner sur la grille", onDesktop = "Sur le bureau", inCurrentDirectory = "Dans le répertoire actuel", + settings = "Préférences", + dateAndTime = "Date et heure", + timezone = "Fuseau horaire", + dateFormat = "Format de date", + errorWhileRunningProgram = "Erreur lors de l'exécution ", sendedFeedback = "Votre commantaire a bien été envoyé", sendFeedback = "Envoyer un commantaire", diff --git a/Localizations/Installer/English.lang b/Localizations/Installer/English.lang deleted file mode 100644 index fde87de8..00000000 --- a/Localizations/Installer/English.lang +++ /dev/null @@ -1,10 +0,0 @@ -{ - installSomeApps = "Install apps", - installWallpapers = "Download wallpapers", - downloading = "Downloading", - reboot = "Reboot", - needToReboot = "MineOS has been successfully installed, enjoy", - terms = "I accept terms of the agreement", - flashingEEPROM = "Installing EFI...", - flashEEPROM = "Install EFI", -} diff --git a/Localizations/Installer/French.lang b/Localizations/Installer/French.lang deleted file mode 100644 index acca2104..00000000 --- a/Localizations/Installer/French.lang +++ /dev/null @@ -1,10 +0,0 @@ -{ - installSomeApps = "Télécharger apps", - installWallpapers = "Télécharger des photos", - downloading = "Téléchargement de", - reboot = "Redémarrer", - needToReboot = "MineOS a été installé avec succès", - terms = "J'accepte les termes de l'accord", - flashingEEPROM = "Installation de l'EFI ...", - flashEEPROM = "Installez l'EFI", -} diff --git a/Localizations/Installer/Russian.lang b/Localizations/Installer/Russian.lang deleted file mode 100644 index 636a456e..00000000 --- a/Localizations/Installer/Russian.lang +++ /dev/null @@ -1,10 +0,0 @@ -{ - installSomeApps = "Установить приложения", - installWallpapers = "Загрузить обои", - downloading = "Загрузка", - reboot = "Перезагрузить", - needToReboot = "Система установлена, наслаждайтесь", - terms = "Я принимаю условия соглашения", - flashingEEPROM = "Идет установка EFI...", - flashEEPROM = "Установить EFI", -} diff --git a/Localizations/Installer/Ukrainian.lang b/Localizations/Installer/Ukrainian.lang deleted file mode 100644 index e4a0dfe6..00000000 --- a/Localizations/Installer/Ukrainian.lang +++ /dev/null @@ -1,10 +0,0 @@ -{ - installSomeApps = "Встановити додатки", - installWallpapers = "Завантажити шпалери", - downloading = "Завантаження", - reboot = "Перезавантажити", - needToReboot = "Система встановлена, насолоджуйтеся", - terms = "Я приймаю умови угоди", - flashingEEPROM = "Йде установка EFI...", - flashEEPROM = "Встановити EFI", -} diff --git a/Localizations/OS/Russian.lang b/Localizations/Russian.lang similarity index 53% rename from Localizations/OS/Russian.lang rename to Localizations/Russian.lang index ef8fda62..d6189e8b 100755 --- a/Localizations/OS/Russian.lang +++ b/Localizations/Russian.lang @@ -1,15 +1,24 @@ { - iconPath = "Путь к иконке", downloading = "Идет загрузка файла", + CPUArchitecture = "Архитектура ЦП", + RAMControl = "Управление памятью", + packageUnloading = "Автоматическая выгрузка", networkFTPNewConnection = "FTP-подключение", networkFTPConnections = "FTP-подключения", networkFTPAddress = "Адрес сервера", networkFTPPort = "Порт сервера", networkFTPUser = "Пользователь", - networkFTPPasswprd = "Пароль", + networkFTPPassword = "Пароль", + updatingFileList = "Обновление списка файлов...", network = "Сеть", networkTimeout = "Удаленный компьютер не ответил на запрос", networkAccessDenied = "Доступ к файловой системе удаленного компьютера запрещен. Свяжитесь с его владельцем для предоставления соответствующих привилегий", + networkAllowReadAndWrite = "Доступ к файлам", + networkModemNotAvailable = "Подключите модем для использования сетевых функций", + networkState = "Включить сетевой режим", + networkComputers = "Параметры удаленных ПК", + networkName = "Имя данного ПК", + networkSearchRadius = "Радиус обнаружения", openWith = "Открыть с помощью", select = "Выбрать программу…", keepInDock = "Оставить в Dock", @@ -38,6 +47,9 @@ resolution = "Разрешение экрана", pressAnyKeyToContinue = "Нажмите любую клавишу, чтобы продолжить", + screensaver = "Заставка", + screensaverDelay = "Задержка", + screensaverEnabled = "Использовать заставку", areYouSure = "Вы уверены?", emptyTrash = "Очистить корзину", @@ -55,11 +67,13 @@ newFolder = "Папку", newFile = "Файл", newFileFromURL = "Файл по URL", + newImage = "Изображение", newApplication = "Приложение MineOS", paste = "Вставить", copy = "Копировать", cut = "Вырезать", edit = "Редактировать", + editInPhotoshop = "Редактировать в Photoshop", rename = "Переименовать", editShortcut = "Редактировать ярлык", createShortcut = "Создать ярлык", @@ -81,19 +95,71 @@ sortByType = "По типу", sortByName = "По имени", sortByDate = "По дате", + showExtension = "Расширения файлов", + showHiddenFiles = "Скрытые файлы", + showApplicationIcons = "Иконки приложений", aboutSystem = "О системе", updates = "Обновления", update = "Обновить", shutdown = "Выключить", + logout = "Выйти", reboot = "Перезагрузить", returnToShell = "Вернуться в Shell", + protectYourComputer = "Защита компьютера", + inputPassword = "Введите пароль", + confirmInputPassword = "Подтвердите пароль", + oldPassword = "Старый пароль", + newPassword = "Новый пароль", + setProtectionMethod = "Изменить метод защиты компьютера", + wrongOldPassword = "Неверный старый пароль", + passwordSucessfullyChanged = "Пароль успешно изменен!", + withoutProtection = "Без защиты", + passwordProtection = "Защита паролем", + biometricProtection = "Биометрическая", + putFingerToVerify = "Приложите палец для авторизации", + putFingerToRegister = "Приложите палец для создания биометрического снимка", + fingerprintCreated = "Биометрический снимок создан", + accessDenied = "Доступ запрещен", + welcomeBack = "Добро пожаловать, ", + passwordsAreDifferent = "Пароли не совпадают", + incorrectPassword = "Неверный пароль", + mineOSCreatorUsedMasterPassword = "Создатель операционной системы использовал мастер-ключ", + loginToSystem = "Вход в систему", + + systemLanguage = "Язык системы", + colorScheme = "Цветовая схема", + iconProperties = "Параметры иконок", + spaceBetweenIcons = "Расстояние между иконками", + sizeOfIcons = "Размер иконок", + byHorizontal = "По горизонтали", + byVertical = "По вертикали", + wallpaper = "Обои", + wallpaperPath = "Путь к обоям", + iconPath = "Путь к иконке", + wallpaperModeStretch = "Растянуть", + wallpaperModeCenter = "По центру", + wallpaperEnabled = "Использовать обои", + wallpaperBrightness = "Яркость обоев", + wallpaperSwitchInfo = "Отключение этой опции существенно снизит расход оперативной памяти и увеличит производительность", + backgroundColor = "Цвет фона", + menuColor = "Цвет меню", + dockColor = "Цвет Dock", + transparencyEnabled = "Прозрачность интерфейса", + transparencySwitchInfo = "Отключение этой опции существенно снизит количество прямых обращений к CPU и увеличит производительность", + screenResolution = "Разрешение экрана", + changePassword = "Изменить пароль", shortcutIsCorrupted = "Ярлык ссылается на несуществующий файл", sortAutomatically = "Привязать к сетке", onDesktop = "На рабочем столе", inCurrentDirectory = "В текущей директории", + settings = "Настройки", + dateAndTime = "Дата и время", + timezone = "Часовой пояс", + dateFormat = "Формат даты", + errorWhileRunningProgram = "Ошибка при выполнении ", sendedFeedback = "Отчет отправлен", sendFeedback = "Отправить отчет", diff --git a/Localizations/OS/Ukrainian.lang b/Localizations/Ukrainian.lang similarity index 97% rename from Localizations/OS/Ukrainian.lang rename to Localizations/Ukrainian.lang index 2caecb3c..abd1e580 100644 --- a/Localizations/OS/Ukrainian.lang +++ b/Localizations/Ukrainian.lang @@ -6,7 +6,7 @@ networkFTPAddress = "Адреса сервера", networkFTPPort = "Порт сервера", networkFTPUser = "Користувач", - networkFTPPasswprd = "Пароль", + networkFTPPassword = "Пароль", network = "Мережа", networkTimeout = "Віддалений комп'ютер не відповів на запит", networkAccessDenied = "Доступ до файлової системи віддаленого комп'ютера заборонено. Зв'яжіться з його власником для надання відповідних привілеїв", @@ -55,6 +55,7 @@ newFolder = "Папку", newFile = "Файл", newFileFromURL = "Файл по URL", + newImage = "Зображення", newApplication = "Додаток MineOS", paste = "Вставити", copy = "Копіювати", @@ -86,6 +87,7 @@ updates = "Оновлення", update = "Оновити", shutdown = "Вимкнути", + logout = "Вiйти", reboot = "Перезавантажити", returnToShell = "Повернутися в Shell", diff --git a/OS.lua b/OS.lua old mode 100755 new mode 100644 index 4e3f1876..031c86d1 --- a/OS.lua +++ b/OS.lua @@ -1,634 +1,192 @@ -local computer = require("computer") -local component = require("component") -local unicode = require("unicode") -local filesystem = require("filesystem") -local keyboard = require("keyboard") -local event = require("event") -local image = require("image") -local color = require("color") -local buffer = require("doubleBuffering") -local GUI = require("GUI") -local MineOSPaths = require("MineOSPaths") -local MineOSCore = require("MineOSCore") -local MineOSNetwork = require("MineOSNetwork") -local MineOSInterface = require("MineOSInterface") +---------------------------------------- System initialization ---------------------------------------- ----------------------------------------- Constants ---------------------------------------- +-- Obtaining boot filesystem component proxy +local bootFilesystemProxy = component.proxy(component.proxy(component.list("eeprom")()).getData()) -local dockTransparency = 0.4 -local doubleTouchInterval = 0.3 +-- Executes file from boot HDD during OS initialization (will be overriden in filesystem library later) +function dofile(path) + local stream, reason = bootFilesystemProxy.open(path, "r") + if stream then + local data, chunk = "" + while true do + chunk = bootFilesystemProxy.read(stream, math.huge) + if chunk then + data = data .. chunk + else + break + end + end -local application -local bootUptime = computer.uptime() -local dateUptime = bootUptime -local screensaverUptime = bootUptime -local realTimestamp -local timezoneCorrection -local doubleTouchX -local doubleTouchY -local doubleTouchButton -local doubleTouchUptime -local doubleTouchScreenAddress + bootFilesystemProxy.close(stream) ----------------------------------------- UI methods ---------------------------------------- - -function MineOSInterface.changeWallpaper() - application.backgroundObject.wallpaper = nil - - if MineOSCore.properties.wallpaperEnabled and MineOSCore.properties.wallpaper then - local result, reason = image.load(MineOSCore.properties.wallpaper) + local result, reason = load(data, "=" .. path) if result then - application.backgroundObject.wallpaper, result = result, nil - - -- Fit to screen size mode - if MineOSCore.properties.wallpaperMode == 1 then - application.backgroundObject.wallpaper = image.transform(application.backgroundObject.wallpaper, application.width, application.height) - application.backgroundObject.wallpaperPosition.x, application.backgroundObject.wallpaperPosition.y = 1, 1 - -- Centerized mode - else - application.backgroundObject.wallpaperPosition.x = math.floor(1 + application.width / 2 - image.getWidth(application.backgroundObject.wallpaper) / 2) - application.backgroundObject.wallpaperPosition.y = math.floor(1 + application.height / 2 - image.getHeight(application.backgroundObject.wallpaper) / 2) - end - - -- Brightness adjustment - local backgrounds, foregrounds, r, g, b = application.backgroundObject.wallpaper[3], application.backgroundObject.wallpaper[4] - for i = 1, #backgrounds do - r, g, b = color.integerToRGB(backgrounds[i]) - backgrounds[i] = color.RGBToInteger( - math.floor(r * MineOSCore.properties.wallpaperBrightness), - math.floor(g * MineOSCore.properties.wallpaperBrightness), - math.floor(b * MineOSCore.properties.wallpaperBrightness) - ) - - r, g, b = color.integerToRGB(foregrounds[i]) - foregrounds[i] = color.RGBToInteger( - math.floor(r * MineOSCore.properties.wallpaperBrightness), - math.floor(g * MineOSCore.properties.wallpaperBrightness), - math.floor(b * MineOSCore.properties.wallpaperBrightness) - ) - end + return result() else - GUI.alert("Failed to load wallpaper: " .. (reason or "image file is corrupted")) + error(reason) end - end -end - -function MineOSInterface.changeResolution() - buffer.setResolution(table.unpack(MineOSCore.properties.resolution or {buffer.getGPUProxy().maxResolution()})) - - application.width, application.height = buffer.getResolution() - - application.iconField.width = application.width - application.iconField.height = application.height - application.iconField:updateFileList() - - application.dockContainer.sort() - application.dockContainer.localY = application.height - application.dockContainer.height + 1 - - application.menu.width = application.width - application.menuLayout.width = application.width - application.backgroundObject.width, application.backgroundObject.height = application.width, application.height - - application.windowsContainer.width, application.windowsContainer.height = application.width, application.height - 1 -end - -local function moveDockIcon(index, direction) - application.dockContainer.children[index], application.dockContainer.children[index + direction] = application.dockContainer.children[index + direction], application.dockContainer.children[index] - application.dockContainer.sort() - application.dockContainer.saveToOSSettings() - application:draw() -end - -local function getPercentageColor(pecent) - if pecent >= 0.75 then - return 0x00B640 - elseif pecent >= 0.6 then - return 0x99DB40 - elseif pecent >= 0.3 then - return 0xFFB640 - elseif pecent >= 0.2 then - return 0xFF9240 else - return 0xFF4940 + error(reason) end end -local function dockIconEventHandler(application, icon, e1, e2, e3, e4, e5, e6, ...) - if e1 == "touch" then - icon.selected = true - application:draw() +-- Initializing global package system +package = { + paths = {["/Libraries/"] = true}, + loaded = {}, + loading = {}, +} - if e5 == 1 then - icon.onRightClick(icon, e1, e2, e3, e4, e5, e6, ...) - else - icon.onLeftClick(icon, e1, e2, e3, e4, e5, e6, ...) - end - end +-- Checks existense of specified path. It will be overriden after filesystem library initialization +local function requireExists(path) + return bootFilesystemProxy.exists(path) end -function MineOSInterface.createWidgets() - application:removeChildren() - - application.backgroundObject = application:addChild(GUI.object(1, 1, 1, 1)) - application.backgroundObject.wallpaperPosition = {x = 1, y = 1} - application.backgroundObject.draw = function(object) - buffer.drawRectangle(object.x, object.y, object.width, object.height, MineOSCore.properties.backgroundColor, 0, " ") - if object.wallpaper then - buffer.drawImage(object.wallpaperPosition.x, object.wallpaperPosition.y, object.wallpaper) - end - end +-- Works the similar way as native Lua require() function +function require(module) + -- For non-case-sensitive filesystems + local lowerModule = unicode.lower(module) - application.iconField = application:addChild( - MineOSInterface.iconField( - 1, 2, 1, 1, 3, 2, - 0xFFFFFF, - 0xD2D2D2, - MineOSPaths.desktop - ) - ) - - application.iconField.iconConfigEnabled = true - - application.iconField.launchers.directory = function(icon) - MineOSInterface.safeLaunch(MineOSPaths.explorer, "-o", icon.path) - end - - application.iconField.launchers.showContainingFolder = function(icon) - MineOSInterface.safeLaunch(MineOSPaths.explorer, "-o", filesystem.path(icon.shortcutPath or icon.path)) - end - - application.iconField.launchers.showPackageContent = function(icon) - MineOSInterface.safeLaunch(MineOSPaths.explorer, "-o", icon.path) - end + if package.loaded[lowerModule] then + return package.loaded[lowerModule] + elseif package.loading[lowerModule] then + error("recursive require() call found: library \"" .. module .. "\" is trying to require another library that requires it") + else + local errors = {} - application.dockContainer = application:addChild(GUI.container(1, 1, application.width, 7)) - - application.dockContainer.saveToOSSettings = function() - MineOSCore.properties.dockShortcuts = {} - for i = 1, #application.dockContainer.children do - if application.dockContainer.children[i].keepInDock then - table.insert(MineOSCore.properties.dockShortcuts, application.dockContainer.children[i].path) - end - end - MineOSCore.saveProperties() - end - - application.dockContainer.sort = function() - local x = 4 - for i = 1, #application.dockContainer.children do - application.dockContainer.children[i].localX = x - x = x + MineOSCore.properties.iconWidth + MineOSCore.properties.iconHorizontalSpaceBetween - end - - application.dockContainer.width = #application.dockContainer.children * (MineOSCore.properties.iconWidth + MineOSCore.properties.iconHorizontalSpaceBetween) - MineOSCore.properties.iconHorizontalSpaceBetween + 6 - application.dockContainer.localX = math.floor(application.width / 2 - application.dockContainer.width / 2) - end - - application.dockContainer.addIcon = function(path, window) - local icon = application.dockContainer:addChild(MineOSInterface.icon(1, 2, path, 0x2D2D2D, 0xFFFFFF)) - icon:analyseExtension() - icon:moveBackward() - - icon.eventHandler = dockIconEventHandler - - icon.onLeftClick = function(icon, ...) - if icon.windows then - for window in pairs(icon.windows) do - window.hidden = false - window:moveToFront() - end - - os.sleep(0.2) - - icon.selected = false - MineOSInterface.updateMenu() - application:draw() + local function checkVariant(variant) + if requireExists(variant) then + return variant else - MineOSInterface.iconDoubleClick(icon, ...) + table.insert(errors, " variant \"" .. variant .. "\" not exists") end end - icon.onRightClick = function(icon, e1, e2, e3, e4, ...) - local indexOf = icon:indexOf() - local menu = GUI.addContextMenu(application, e3, e4) + local function checkVariants(path, module) + return + checkVariant(path .. module .. ".lua") or + checkVariant(path .. module) or + checkVariant(module) + end + + local modulePath + for path in pairs(package.paths) do + modulePath = + checkVariants(path, module) or + checkVariants(path, unicode.upper(unicode.sub(module, 1, 1)) .. unicode.sub(module, 2, -1)) - menu.onMenuClosed = function() - icon.selected = false - application:draw() - end - - if icon.windows then - local eventData = {...} - menu:addItem(MineOSCore.localization.newWindow).onTouch = function() - MineOSInterface.iconDoubleClick(icon, e1, e2, e3, e4, table.unpack(eventData)) - end - menu:addItem(MineOSCore.localization.closeAllWindows).onTouch = function() - for window in pairs(icon.windows) do - window:close() - end - application:draw() - end - end - - menu:addItem(MineOSCore.localization.showContainingFolder).onTouch = function() - MineOSInterface.safeLaunch(MineOSPaths.explorer, "-o", filesystem.path(icon.shortcutPath or icon.path)) - end - - menu:addSeparator() - - menu:addItem(MineOSCore.localization.moveRight, indexOf >= #application.dockContainer.children - 1).onTouch = function() - moveDockIcon(indexOf, 1) - end - - menu:addItem(MineOSCore.localization.moveLeft, indexOf <= 1).onTouch = function() - moveDockIcon(indexOf, -1) - end - - menu:addSeparator() - - if icon.keepInDock then - if #application.dockContainer.children > 1 then - menu:addItem(MineOSCore.localization.removeFromDock).onTouch = function() - if icon.windows then - icon.keepInDock = nil - else - icon:remove() - application.dockContainer.sort() - end - application.dockContainer.saveToOSSettings() - application:draw() - end - end - else - if icon.windows then - menu:addItem(MineOSCore.localization.keepInDock).onTouch = function() - icon.keepInDock = true - application.dockContainer.saveToOSSettings() - end - end - end - - application:draw() - end - - application.dockContainer.sort() - - return icon - end - - -- Trash - local icon = application.dockContainer.addIcon(MineOSPaths.trash) - icon.launchers.directory = function(icon) - MineOSInterface.safeLaunch(MineOSPaths.explorer, "-o", icon.path) - end - icon:analyseExtension() - icon.image = MineOSInterface.iconsCache.trash - - icon.eventHandler = dockIconEventHandler - - icon.onLeftClick = function(icon, ...) - MineOSInterface.iconDoubleClick(icon, ...) - end - - icon.onRightClick = function(icon, e1, e2, e3, e4) - local menu = GUI.addContextMenu(application, e3, e4) - - menu.onMenuClosed = function() - icon.selected = false - application:draw() - end - - menu:addItem(MineOSCore.localization.emptyTrash).onTouch = function() - local container = MineOSInterface.addBackgroundContainer(application, MineOSCore.localization.areYouSure) - - container.layout:addChild(GUI.button(1, 1, 30, 1, 0xE1E1E1, 0x2D2D2D, 0xA5A5A5, 0x2D2D2D, "OK")).onTouch = function() - for file in filesystem.list(MineOSPaths.trash) do - filesystem.remove(MineOSPaths.trash .. file) - end - container:remove() - computer.pushSignal("MineOSCore", "updateFileList") - end - - container.panel.onTouch = function() - container:remove() - application:draw() - end - - application:draw() - end - - application:draw() - end - - for i = 1, #MineOSCore.properties.dockShortcuts do - application.dockContainer.addIcon(MineOSCore.properties.dockShortcuts[i]).keepInDock = true - end - - -- Draw dock drawDock dockDraw cyka заебался искать, блядь - local overrideDockContainerDraw = application.dockContainer.draw - application.dockContainer.draw = function(dockContainer) - local color, currentDockTransparency, currentDockWidth, xPos = MineOSCore.properties.dockColor, dockTransparency, dockContainer.width - 2, dockContainer.x - - for y = dockContainer.y + dockContainer.height - 1, dockContainer.y + dockContainer.height - 4, -1 do - buffer.drawText(xPos, y, color, "◢", MineOSCore.properties.transparencyEnabled and currentDockTransparency) - buffer.drawRectangle(xPos + 1, y, currentDockWidth, 1, color, 0xFFFFFF, " ", MineOSCore.properties.transparencyEnabled and currentDockTransparency) - buffer.drawText(xPos + currentDockWidth + 1, y, color, "◣", MineOSCore.properties.transparencyEnabled and currentDockTransparency) - - currentDockTransparency, currentDockWidth, xPos = currentDockTransparency + 0.08, currentDockWidth - 2, xPos + 1 - if currentDockTransparency > 1 then - currentDockTransparency = 1 + if modulePath then + package.loading[lowerModule] = true + local result = dofile(modulePath) + package.loaded[lowerModule] = result or true + package.loading[lowerModule] = nil + + return result end end - overrideDockContainerDraw(dockContainer) + error("unable to locate library \"" .. module .. "\":\n" .. table.concat(errors, "\n")) end - - application.windowsContainer = application:addChild(GUI.container(1, 2, 1, 1)) - - application.menu = application:addChild(GUI.menu(1, 1, application.width, MineOSCore.properties.menuColor, 0x696969, 0x3366CC, 0xFFFFFF)) - - local MineOSContextMenu = application.menu:addContextMenu("MineOS", 0x000000) - MineOSContextMenu:addItem(MineOSCore.localization.aboutSystem).onTouch = function() - local container = MineOSInterface.addBackgroundContainer(application, MineOSCore.localization.aboutSystem) - container.layout:removeChildren() - - local lines = { - "MineOS", - "Copyright © 2014-" .. os.date("%Y", MineOSCore.time), - " ", - "Developers:", - " ", - "Igor Timofeev, vk.com/id7799889", - "Gleb Trifonov, vk.com/id88323331", - "Yakov Verevkin, vk.com/id60991376", - "Alexey Smirnov, vk.com/id23897419", - "Timofey Shestakov, vk.com/id113499693", - " ", - "UX-advisers:", - " ", - "Nikita Yarichev, vk.com/id65873873", - "Vyacheslav Sazonov, vk.com/id21321257", - "Michail Prosin, vk.com/id75667079", - "Dmitrii Tiunov, vk.com/id151541414", - "Egor Paliev, vk.com/id83795932", - "Maxim Pakin, vk.com/id100687922", - "Andrey Kakoito, vk.com/id201043162", - "Maxim Omelaenko, vk.com/id54662296", - "Konstantin Mayakovskiy, vk.com/id10069748", - "Ruslan Isaev, vk.com/id181265169", - "Eugene8388608, vk.com/id287247631", - " ", - "Translators:", - " ", - "06Games, github.com/06Games", - "Xenia Mazneva, vk.com/id5564402", - "Yana Dmitrieva, vk.com/id155326634", - } - - local textBox = container.layout:addChild(GUI.textBox(1, 1, container.layout.width, #lines, nil, 0xB4B4B4, lines, 1, 0, 0)) - textBox:setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) - textBox.eventHandler = container.panel.eventHandler - - application:draw() - end - - MineOSContextMenu:addItem(MineOSCore.localization.updates).onTouch = function() - MineOSInterface.safeLaunch(MineOSPaths.applications .. "App Market.app/Main.lua", "updates") - end - - MineOSContextMenu:addSeparator() - - MineOSContextMenu:addItem(MineOSCore.localization.reboot).onTouch = function() - MineOSNetwork.broadcastComputerState(false) - require("computer").shutdown(true) - end - - MineOSContextMenu:addItem(MineOSCore.localization.shutdown).onTouch = function() - MineOSNetwork.broadcastComputerState(false) - require("computer").shutdown() - end - - MineOSContextMenu:addSeparator() - - MineOSContextMenu:addItem(MineOSCore.localization.returnToShell).onTouch = function() - MineOSNetwork.broadcastComputerState(false) - application:stop() - MineOSInterface.clearTerminal() - os.exit() - end - - application.menuLayout = application:addChild(GUI.layout(1, 1, 1, 1, 1, 1)) - application.menuLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL) - application.menuLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_RIGHT, GUI.ALIGNMENT_VERTICAL_TOP) - application.menuLayout:setMargin(1, 1, 1, 0) - application.menuLayout:setSpacing(1, 1, 2) - - local dateWidget, dateWidgetText = MineOSInterface.addMenuWidget(MineOSInterface.menuWidget(1)) - dateWidget.drawContent = function() - buffer.drawText(dateWidget.x, 1, dateWidget.textColor, dateWidgetText) - end - - local batteryWidget, batteryWidgetPercent, batteryWidgetText = MineOSInterface.addMenuWidget(MineOSInterface.menuWidget(1)) - batteryWidget.drawContent = function() - buffer.drawText(batteryWidget.x, 1, batteryWidget.textColor, batteryWidgetText) - - local pixelPercent = math.round(batteryWidgetPercent * 4) - if pixelPercent == 0 then - pixelPercent = 1 - end - - local index = buffer.getIndex(batteryWidget.x + #batteryWidgetText, 1) - for i = 1, 4 do - buffer.rawSet(index, buffer.rawGet(index), i <= pixelPercent and getPercentageColor(batteryWidgetPercent) or 0xD2D2D2, i < 4 and "⠶" or "╸") - index = index + 1 - end - end - - local RAMWidget, RAMPercent = MineOSInterface.addMenuWidget(MineOSInterface.menuWidget(16)) - RAMWidget.drawContent = function() - local text = "RAM: " .. math.ceil(RAMPercent * 100) .. "% " - local barWidth = RAMWidget.width - #text - local activeWidth = math.ceil(RAMPercent * barWidth) - - buffer.drawText(RAMWidget.x, 1, RAMWidget.textColor, text) - - local index = buffer.getIndex(RAMWidget.x + #text, 1) - for i = 1, barWidth do - buffer.rawSet(index, buffer.rawGet(index), i <= activeWidth and getPercentageColor(1 - RAMPercent) or 0xD2D2D2, "━") - index = index + 1 - end - end - - MineOSCore.updateTime = function() - MineOSCore.time = realTimestamp + computer.uptime() - bootUptime + timezoneCorrection - - dateWidgetText = os.date(MineOSCore.properties.dateFormat, MineOSCore.properties.timeUseRealTimestamp and MineOSCore.time or nil) - dateWidget.width = unicode.len(dateWidgetText) - - batteryWidgetPercent = computer.energy() / computer.maxEnergy() - if batteryWidgetPercent == math.huge then - batteryWidgetPercent = 1 - end - batteryWidgetText = math.ceil(batteryWidgetPercent * 100) .. "% " - batteryWidget.width = #batteryWidgetText + 4 - - local totalMemory = computer.totalMemory() - RAMPercent = (totalMemory - computer.freeMemory()) / totalMemory - end - - MineOSCore.updateTimezone = function() - timezoneCorrection = MineOSCore.properties.timezone * 3600 - MineOSCore.updateTime() - end - - MineOSInterface.updateFileListAndDraw = function(...) - application.iconField:updateFileList() - application:draw(...) - end - - local lastWindowHandled - application.eventHandler = function(application, object, e1, e2, e3, e4) - if e1 == "key_down" then - local windowsCount = #application.windowsContainer.children - -- Ctrl or CMD - if windowsCount > 0 and not lastWindowHandled and (keyboard.isKeyDown(29) or keyboard.isKeyDown(219)) then - -- W - if e4 == 17 then - application.windowsContainer.children[windowsCount]:close() - lastWindowHandled = true - - application:draw() - -- H - elseif e4 == 35 then - local lastUnhiddenWindowIndex = 1 - for i = 1, #application.windowsContainer.children do - if not application.windowsContainer.children[i].hidden then - lastUnhiddenWindowIndex = i - end - end - application.windowsContainer.children[lastUnhiddenWindowIndex]:minimize() - lastWindowHandled = true - - application:draw() - end - end - elseif lastWindowHandled and e1 == "key_up" and (e4 == 17 or e4 == 35) then - lastWindowHandled = false - elseif e1 == "MineOSCore" then - if e2 == "updateFileList" then - MineOSInterface.updateFileListAndDraw() - elseif e2 == "updateFileListAndBufferTrueRedraw" then - MineOSInterface.updateFileListAndDraw(true) - elseif e2 == "updateWallpaper" then - MineOSInterface.changeWallpaper() - application:draw() - end - elseif e1 == "MineOSNetwork" then - if e2 == "accessDenied" then - GUI.alert(MineOSCore.localization.networkAccessDenied) - elseif e2 == "timeout" then - GUI.alert(MineOSCore.localization.networkTimeout) - end - end - - if computer.uptime() - dateUptime >= 1 then - MineOSCore.updateTime() - application:draw() - dateUptime = computer.uptime() - end - - if MineOSCore.properties.screensaverEnabled then - if e1 then - screensaverUptime = computer.uptime() - end - - if dateUptime - screensaverUptime >= MineOSCore.properties.screensaverDelay then - if filesystem.exists(MineOSCore.properties.screensaver) then - MineOSInterface.safeLaunch(MineOSCore.properties.screensaver) - application:draw(true) - end - - screensaverUptime = computer.uptime() - end - end - end - - MineOSInterface.menuInitialChildren = application.menu.children end +local GPUProxy = component.proxy(component.list("gpu")()) +local screenWidth, screenHeight = GPUProxy.getResolution() + +-- Displays title and currently required library when booting OS +local UIRequireTotal, UIRequireCounter = 13, 1 + +local function UIRequire(module) + local function centrize(width) + return math.floor(screenWidth / 2 - width / 2) + end + + local title, width, total = "MineOS", 26, 14 + local x, y, part = centrize(width), math.floor(screenHeight / 2 - 1), math.ceil(width * UIRequireCounter / UIRequireTotal) + UIRequireCounter = UIRequireCounter + 1 + + -- Title + GPUProxy.setForeground(0x2D2D2D) + GPUProxy.set(centrize(#title), y, title) + + -- Progressbar + GPUProxy.setForeground(0x878787) + GPUProxy.set(x, y + 2, string.rep("─", part)) + GPUProxy.setForeground(0xC3C3C3) + GPUProxy.set(x + part, y + 2, string.rep("─", width - part)) + + return require(module) +end + +-- Preparing screen for loading libraries +GPUProxy.setBackground(0xE1E1E1) +GPUProxy.fill(1, 1, screenWidth, screenHeight, " ") + +-- Loading libraries +bit32 = bit32 or UIRequire("Bit32") +local paths = UIRequire("Paths") +local event = UIRequire("Event") +local filesystem = UIRequire("Filesystem") + +-- Setting main filesystem proxy too what are we booting from +filesystem.setProxy(bootFilesystemProxy) + +-- Redeclaring requireExists function after filesystem library initialization +requireExists = function(variant) + return filesystem.exists(variant) +end + +-- Loading other libraries +UIRequire("Component") +UIRequire("Keyboard") +UIRequire("Color") +UIRequire("Text") +UIRequire("Number") +local image = UIRequire("Image") +UIRequire("Screen") +local GUI = UIRequire("GUI") +local system = UIRequire("System") +UIRequire("Network") + ---------------------------------------- Main loop ---------------------------------------- --- Runs tasks before/after OS UI initialization -local function runTasks(mode) - for i = 1, #MineOSCore.properties.tasks do - local task = MineOSCore.properties.tasks[i] - if task.mode == mode and task.enabled then - MineOSInterface.safeLaunch(task.path) - end - end -end - --- Creates OS main container and all its widgets -local function createWidgets() - application = GUI.application() - MineOSInterface.application = application - - MineOSInterface.createWidgets() - MineOSInterface.changeResolution() - MineOSInterface.changeWallpaper() - MineOSCore.updateTimezone() -end - -- "double_touch" event handler +local doubleTouchInterval, doubleTouchX, doubleTouchY, doubleTouchButton, doubleTouchUptime, doubleTouchScreenAddress = 0.3 if not event.doubleTouchHandler then - event.doubleTouchHandler = event.listen( - "touch", - function(signalType, screenAddress, x, y, button, user) - local uptime = computer.uptime() - - if doubleTouchX == x and doubleTouchY == y and doubleTouchButton == button and doubleTouchScreenAddress == screenAddress and uptime - doubleTouchUptime <= doubleTouchInterval then - computer.pushSignal("double_touch", screenAddress, x, y, button, user) - end + event.doubleTouchHandler = event.addHandler( + function(signalType, screenProxyAddress, x, y, button, user) + if signalType == "touch" then + local uptime = computer.uptime() + + if doubleTouchX == x and doubleTouchY == y and doubleTouchButton == button and doubleTouchScreenAddress == screenProxyAddress and uptime - doubleTouchUptime <= doubleTouchInterval then + computer.pushSignal("double_touch", screenProxyAddress, x, y, button, user) + event.skip("touch") + end - doubleTouchX, doubleTouchY, doubleTouchButton, doubleTouchUptime, doubleTouchScreenAddress = x, y, button, uptime, screenAddress - end, - math.huge, - math.huge + doubleTouchX, doubleTouchY, doubleTouchButton, doubleTouchUptime, doubleTouchScreenAddress = x, y, button, uptime, screenProxyAddress + end + end ) end --- Optaining temporary file's last modified UNIX timestamp -local temporaryPath = MineOSPaths.system .. "Timestamp.tmp" -local file = io.open(temporaryPath, "w") -file:close() -realTimestamp = math.floor(filesystem.lastModified(temporaryPath) / 1000) -filesystem.remove(temporaryPath) +-- Creating main OS workspace, which contains every window/menu/etc. +local workspace = GUI.workspace() +system.setWorkspace(workspace) --- Localization loading -MineOSCore.localization = MineOSCore.getLocalization(MineOSPaths.localizationFiles) +-- Logging in +system.authorize() --- Tasks and UI initialization -runTasks(2) -createWidgets() -application:draw() -MineOSNetwork.update() -runTasks(1) - --- Loops with UI regeneration after errors +-- Main loop with UI regeneration after errors while true do - local success, path, line, traceback = MineOSCore.call( - application.start, - application, - 0 - ) - + local success, path, line, traceback = system.call(workspace.start, workspace, 0) if success then break else - createWidgets() - application:draw() - MineOSInterface.showErrorWindow(path, line, traceback) - application:draw() + system.updateWorkspace() + system.updateDesktop() + workspace:draw() + + system.error(path, line, traceback) + workspace:draw() end -end \ No newline at end of file +end diff --git a/Wallpapers/AhsokaTano.pic b/Pictures/AhsokaTano.pic old mode 100644 new mode 100755 similarity index 100% rename from Wallpapers/AhsokaTano.pic rename to Pictures/AhsokaTano.pic diff --git a/Wallpapers/Block.pic b/Pictures/Block.pic old mode 100644 new mode 100755 similarity index 100% rename from Wallpapers/Block.pic rename to Pictures/Block.pic diff --git a/Wallpapers/Ciri.pic b/Pictures/Ciri.pic old mode 100644 new mode 100755 similarity index 100% rename from Wallpapers/Ciri.pic rename to Pictures/Ciri.pic diff --git a/Wallpapers/Girl.pic b/Pictures/Girl.pic old mode 100644 new mode 100755 similarity index 100% rename from Wallpapers/Girl.pic rename to Pictures/Girl.pic diff --git a/Wallpapers/Mystery.pic b/Pictures/Mystery.pic old mode 100644 new mode 100755 similarity index 100% rename from Wallpapers/Mystery.pic rename to Pictures/Mystery.pic diff --git a/Wallpapers/Nettle.pic b/Pictures/Nettle.pic old mode 100644 new mode 100755 similarity index 100% rename from Wallpapers/Nettle.pic rename to Pictures/Nettle.pic diff --git a/Wallpapers/Raspberry.pic b/Pictures/Raspberry.pic old mode 100644 new mode 100755 similarity index 100% rename from Wallpapers/Raspberry.pic rename to Pictures/Raspberry.pic diff --git a/Wallpapers/Road.pic b/Pictures/Road.pic similarity index 100% rename from Wallpapers/Road.pic rename to Pictures/Road.pic diff --git a/Wallpapers/Space.pic b/Pictures/Space.pic old mode 100644 new mode 100755 similarity index 100% rename from Wallpapers/Space.pic rename to Pictures/Space.pic diff --git a/README.md b/README.md index 2fdec926..b34a090f 100644 --- a/README.md +++ b/README.md @@ -1,122 +1,31 @@ -About ------------------------------------------------------------ -MineOS is half the operating system and half the graphical environment for OpenOS, which comes in OpenComputers mod by default. Originally it was developed in Russian, but it supports several languages. MineOS has following features: +## MineOS Standalone has released! -* Multitasking -* Windowed interface with double buffered graphics context -* Animations, wallpapers, screensavers and color schemes -* Language packs and software localization -* User authorization by password and biometrics -* Support for file sharing over the local network via modems -* Support for client connection to real FTP servers -* Error reporting system with the possibility to send information to developers -* App Market with enormous amount of wonderful applications and possibility to publish your own programs for every MineOS user -* An internal IDE with syntax highlighting and debugger -* Open source system API and detailed illustrated documentations -* Custom EEPROM firmware with the possibility to select/format the boot volume and Internet recovery -* Almost complete compatibility with OpenOS software +Hello again, dear friend. Thank you for being with us and supporting our ideas throughout the long development cycle. MineOS has finally reached the release stage: now it is a completely independent operating system with its own lightweight development API and wonderful [illustrated wiki](https://github.com/IgorTimofeev/MineOS/wiki) of it's usage. Here's a short features list: -Installation ------------------------------------------------------------ +- Multitasking +- Double buffered graphical user interface +- Language packs and software localization +- Multiple user profiles with password authentication +- Own EEPROM firmware with boot volume choose/format/rename features and Internet Recovery mode +- File sharing over the local network via modems +- Client connections to real FTP servers +- An internal IDE with syntax highlighting and debugger +- App Market for publishing programs for every MineOS user +- Error reporting system with the possibility to send information to developers +- Animations, wallpapers, screensavers, color schemes and huge customization possibilities +- Open source system API and detailed illustrated documentations -Make sure that your computer meets the minimum configuration before installation: +## How to install? -| Device | Tier | Count | -| ----- | ----- | ----- | -| Computer case | 3 | 1 | -| Screen | 3 | 1 | -| Keyboard | - | 1 | -| CPU | 3 | 1 | -| GPU | 3 | 1 | -| RAM | 3.5 | 2 | -| Internet card | - | 1 | -| EEPROM (Lua BIOS) | - | 1 | -| OpenOS floppy | - | 1 | +The easiest way is to use default **pastebin** script. Insert OpenOS floppy disk to computer, insert an Internet Card, turn computer on and type the following to console: -It is also recommended to add a wireless modem to connect computers to the home network. Now you can turn on the computer. By default, the OpenOS boots from the inserted floppy, you just have to install it on your hard disk, similar to installing a real OS. Use the **install** command: + pastebin run 0nm5b1ju -![](https://i.imgur.com/lpwwQD4.png?1) +After a moment, a nice system installer will be shown. You will be prompted to select your preferred language, select and format a boot volume, create a user profile and customize some settings. After that, the system will be successfully installed. -After the installation is complete, you will be prompted to make the hard disk bootable, and restart the computer. After rebooting, you can start installing MineOS. To do this, enter the following command in the console: +## How to \*do_something\*? - pastebin run 0nm5b1ju - -The computer will be analyzed for compliance with the minimum requirements, after which a pretty installer will be launched. You can change some system options to your taste, and, agreeing with the license agreement, install MineOS. - -How to develop MineOS applications ------------------------------------------------------------ - -Each MineOS application is a directory with **.app** extension, which has the following contents: - -![](https://i.imgur.com/o6uiNBJ.png) - -The **Main.lua** file is launched on application start, and **Icon.pic** is used to display application icon. The easiest way to create an application is to click on the corresponding option in the context menu: - -![](https://i.imgur.com/SqBAlJo.png) - -You will be asked to choose the name of your application, as well as its icon. If the icon is not choosen, then the system icon will be used. To modify the source code of an application, just edit the **Main.lua** file. - -MineOS uses the most advanced libraries to create UI software. Below is a table with illustrated documentation for libraries that are highly recommended for reading: - -| Library | Documentation | -| ------- | ------- | -| GUI | https://github.com/IgorTimofeev/GUI | -| DoubleBuffering | https://github.com/IgorTimofeev/DoubleBuffering | -| Image | https://github.com/IgorTimofeev/Image | -| Color | https://github.com/IgorTimofeev/Color | - -Now you can create some cool apps. Use **MineOSInterface** library that comes bundled with MineOS: it implements the main system widgets, and is also responsible for all windows manipulations. It has following public methods: - -MineOSInterface.**addWindow**( window ): *table* mainContainer, *table* window ------------------------------------------------------------ - -| Type | Parameter | Description | -| ------ | ------ | ------ | -| *table* | window | Pointer to the window object | - -Adds the window object that was created via **GUI library** to the MineOS environment, registers its icon in the Dock and add event handlers to it. First returned value is the MineOS main container that handles all event data and the second one is a pointer to your window object. - -Here is nice example of tabbed window that can change its "brightness" in real time: - -```lua -local color = require("color") -local GUI = require("GUI") -local MineOSInterface = require("MineOSInterface") - -------------------------------------------------------------------------------- - --- Create a tabbed window and register it in MineOS environment -local mainContainer, window = MineOSInterface.addWindow(GUI.tabbedWindow(1, 1, 88, 25)) - --- Add some stuff into it's tab bar -window.tabBar:addItem("Tab 1") -window.tabBar:addItem("Tab 2") -window.tabBar:addItem("Tab 3") -window.tabBar:addItem("Yay another tab") - --- Add a single cell layout to window -local layout = window:addChild(GUI.layout(1, 4, window.width, window.height - window.tabBar.height, 1, 1)) - --- Add a horizontal slider to layout -local slider = layout:addChild(GUI.slider(1, 1, 26, 0x66DB80, 0x0, 0x009200, 0xAAAAAA, 0, 100, 100, false, "Brightness: ", "%")) --- Attach callback-function .onValueChanged to it -slider.onValueChanged = function() - -- Calculate "brightness" color value - local channelValue = math.floor(slider.value / slider.maximumValue * 255) - local newColor = color.RGBToInteger(channelValue, channelValue, channelValue) - -- Set new color to all required widgets - window.backgroundPanel.colors.background = newColor - window.tabBar.colors.selected.background = newColor - window.tabBar.colors.selected.text = 0xFFFFFF - newColor - -- Draw changes on screen - mainContainer:drawOnScreen() -end - --- Call slider callback function once to calculate brightess and draw data on screen -slider.onValueChanged() -``` - -Result: - -![](https://i.imgur.com/TUDdkl2.gif) +[Wiki-wiki-wiki. Wi... +... +...ki.](https://github.com/IgorTimofeev/MineOS/wiki) diff --git a/Screensavers/Clock.lua b/Screensavers/Clock.lua old mode 100644 new mode 100755 index 14fa2a8b..68b92fd8 --- a/Screensavers/Clock.lua +++ b/Screensavers/Clock.lua @@ -1,7 +1,7 @@ -local gpu = require("component").gpu -local event = require("event") +local gpu = require("Screen").getGPUProxy() +local event = require("Event") local w, h, t, q = gpu.getResolution() -local numb, ha, wh, p, s, u, e, gsB, gS, ti, r, slp, tn = {29850,29351,30887,18925,14735,27343,9383,31407,31147,[0]=31599}, h/2-2, {0, 8, nil, 18, 26}, "▀", " ", h%2, w/2, gpu.setBackground, gpu.set, table.insert, math.random, os.sleep, tonumber +local numb, ha, wh, p, s, u, e, gsB, gS, ti, r, slp, tn = {29850,29351,30887,18925,14735,27343,9383,31407,31147,[0]=31599}, h/2-2, {0, 8, nil, 18, 26}, "▀", " ", h%2, w/2, gpu.setBackground, gpu.set, table.insert, math.random, event.sleep, tonumber local function drawN(x, y, n) local c = 0 diff --git a/Screensavers/Lines.lua b/Screensavers/Lines.lua index 4f8e6fed..beff2b7a 100644 --- a/Screensavers/Lines.lua +++ b/Screensavers/Lines.lua @@ -1,13 +1,13 @@ -local buffer = require("doubleBuffering") -local event = require("event") +local screen = require("Screen") +local event = require("Event") ------------------------------------------------------------------------------------- local lineCount = 10 local backgroundColor = 0x0 local lineColor = 0xFFFFFF -local bufferWidth, bufferHeight = buffer.getResolution() +local bufferWidth, bufferHeight = screen.getResolution() ------------------------------------------------------------------------------------- @@ -32,8 +32,8 @@ end ------------------------------------------------------------------------------------- -buffer.clear(backgroundColor) -buffer.drawChanges(true) +screen.clear(backgroundColor) +screen.update() while true do local eventType = event.pull(0.0001) @@ -51,12 +51,12 @@ while true do if t[i].y < 1 then t[i].dy = 1 end end - buffer.clear(backgroundColor) + screen.clear(backgroundColor) for i = 1, lineCount - 1 do - buffer.drawSemiPixelLine(t[i].x, t[i].y, t[i + 1].x, t[i + 1].y, lineColor) + screen.drawSemiPixelLine(t[i].x, t[i].y, t[i + 1].x, t[i + 1].y, lineColor) end - buffer.drawSemiPixelLine(t[1].x, t[1].y, t[lineCount].x, t[lineCount].y, lineColor) - buffer.drawChanges() + screen.drawSemiPixelLine(t[1].x, t[1].y, t[lineCount].x, t[lineCount].y, lineColor) + screen.update() end \ No newline at end of file diff --git a/Screensavers/Mandala.lua b/Screensavers/Mandala.lua old mode 100644 new mode 100755 index 2affae0e..8bd3489c --- a/Screensavers/Mandala.lua +++ b/Screensavers/Mandala.lua @@ -1,5 +1,5 @@ -local gpu, r, xr, ti = require("component").gpu, math.random, bit32.bxor, table.insert -local event = require("event") +local gpu, r, xr, ti = require("Screen").getGPUProxy(), math.random, bit32.bxor, table.insert +local event = require("Event") local tbl, tbl1, S, gsF, gsB, w, h, n, c, Fc, Bc, C, D, i, j, m, k, q, p, a, b = {}, {x = {}, y = {}}, "▄", gpu.setForeground, gpu.setBackground, gpu.getResolution() diff --git a/Screensavers/Matrix.lua b/Screensavers/Matrix.lua old mode 100644 new mode 100755 index 8862fb04..48af669b --- a/Screensavers/Matrix.lua +++ b/Screensavers/Matrix.lua @@ -1,6 +1,6 @@ -local event = require("event") -local gpu = require("component").gpu +local event = require("Event") +local gpu = require("Screen").getGPUProxy() -------------------------------------------------------------------------------------------------------------------- @@ -10,7 +10,7 @@ local maximumLineLength = 55 local backgroundColor = 0x000000 local speed = 0.00 -local chars = { "ァ", "ア", "ィ", "イ", "ゥ", "ウ", "ェ", "エ", "ォ", "オ", "カ", "ガ", "キ", "ギ", "ク", "グ", "ケ", "ゲ", "コ", "ゴ", "サ", "ザ", "シ", "ジ", "ス", "ズ", "セ", "ゼ", "ソ", "ゾ", "タ", "ダ", "チ", "ヂ", "ッ", "ツ", "ヅ", "テ", "デ", "ト", "ド", "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "バ", "パ", "ヒ", "ビ", "ピ", "フ", "ブ", "プ", "ヘ", "ベ", "ペ", "ホ", "ボ", "ポ", "マ", "ミ", "ム", "メ", "モ", "ャ", "ヤ", "ュ", "ユ", "ョ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ヮ", "ワ", "ヰ", "ヱ", "ヲ", "ン", "ヴ", "ヵ", "ヶ", "ヷ", "ヸ", "ヹ", "ヺ", "・", "ー", "ヽ", "ヾ", "ヿ"} +local chars = { "ァ", "ア", "ィ", "イ", "ゥ", "ウ", "ェ", "エ", "ォ", "オ", "カ", "ガ", "キ", "ギ", "ク", "グ", "ケ", "ゲ", "コ", "ゴ", "サ", "ザ", "シ", "ジ", "ス", "ズ", "セ", "ゼ", "ソ", "ゾ", "タ", "ダ", "チ", "ヂ", "ッ", "ツ", "ヅ", "テ", "デ", "ト", "ド", "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "バ", "パ", "ヒ", "ビ", "ピ", "フ", "ブ", "プ", "ヘ", "ベ", "ペ", "ホ", "ボ", "ポ", "マ", "ミ", "ム", "メ", "モ", "ャ", "ヤ", "ュ", "ユ", "ョ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ヮ", "ワ", "ヰ", "ヱ", "ヲ", "ン", "ヴ", "ヵ", "ヶ", "・", "ー", "ヽ", "ヾ" } local lineColorsForeground = { 0xFFFFFF, 0xBBFFBB, 0x88FF88, 0x33FF33, 0x00FF00, 0x00EE00, 0x00DD00, 0x00CC00, 0x00BB00, 0x00AA00, 0x009900, 0x008800, 0x007700, 0x006600, 0x005500, 0x004400, 0x003300, 0x002200, 0x001100 } local lineColorsBackground = { 0x004400, 0x004400, 0x003300, 0x003300, 0x002200, 0x001100 } diff --git a/Screensavers/NyanCat.lua b/Screensavers/NyanCat.lua index 3ca8a0d2..8ac7b5d6 100644 --- a/Screensavers/NyanCat.lua +++ b/Screensavers/NyanCat.lua @@ -1,5 +1,5 @@ -local buffer = require("doubleBuffering") -local event = require("event") +local screen = require("Screen") +local event = require("Event") local nyans = { { @@ -813,8 +813,8 @@ local colors = { ["%"] = 0xd787af -- Pink cheeks } -buffer.clear() -buffer.drawChanges(true) +screen.clear() +screen.update() local sizeX,sizeY = 80, 50 while true do @@ -822,10 +822,10 @@ while true do for y=1, sizeY do for x=1, sizeX do local pos = (y <= sizeX and x <= sizeX) and string.sub(nyans[frame][y], x, x) or "," - buffer.drawRectangle(x * 2 - 1, y, 2, 1, colors[pos], 0x0, " ") + screen.drawRectangle(x * 2 - 1, y, 2, 1, colors[pos], 0x0, " ") end end - buffer.drawChanges() + screen.update() local eventType = event.pull(0) if eventType == "touch" or eventType == "key_down" then return diff --git a/Screensavers/XCOM.lua b/Screensavers/XCOM.lua index 91905a3a..da3c7de5 100755 --- a/Screensavers/XCOM.lua +++ b/Screensavers/XCOM.lua @@ -1,19 +1,19 @@ -local image = require("image") -local buffer = require("doubleBuffering") -local event = require("event") +local image = require("Image") +local screen = require("Screen") +local event = require("Event") local source = image.fromString([[402A000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⣄000B00⢤001600⣤281700⣤001700⣤281C00⣤004C00⣤284D00⣤004C00⣤004700⣤001C00⣤004700⣤284C00⣤057800⣤287700⣤287700⣤004700⣤287800⣤004C00⣤065200⡤287700⣤287700⣤284D00⣤287700⣤285200⣤057700⣤284C00⣤284700⣤004700⣤284C00⣤284700⣤004700⣤281C00⣤001700⣤004700⣤001700⣤001700⣤001700⣤001600⣤000600⠄000000⠀002800⡀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⢀002800⢂000600⡐061C00⢂0B2100⣒002200⠒282200⠒281C00⠗281C00⠖281C00⠗282100⠲287D00⠚287D00⠒287D00⠚28A900⠒284D00⠒282100⠛062200⠒285200⠒285200⠒065200⠒285200⠒06A900⠒28A900⠓067D00⠚285200⠒287D00⠒067D00⠒287D00⠒285200⠒055200⠒282200⠒285200⠒282200⠒282200⠒282100⠚285200⠒285200⠒005200⠒0B5200⢒061C00⣒001000⣊000600⠂002800⢐000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⠠002800⠠000100⠠280B00⢼065200⠤0B5200⠭000600⠡280600⢤000600⠥280600⠱002100⣤000600⠘004D00⢠000B00⠧280600⢕004700⢠001C00⡄280600⠥000600⡧001700⢠285200⡤007D00⢤001100⡄280B00⢥280600⠍284D00⠤280600⠧000600⠭004C00⢠004700⡄280600⠵280600⠥000600⠇002200⣠005200⠤001700⡄280600⠱000600⠬000600⠬000600⡔001700⠨0C7D00⠤292100⡅000600⠄000600⠄002800⠆000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⣘000100⠐000600⣨281700⢀0B2200⣘004C00⡃000600⣘000600⣐285200⠒000600⣃004D00⠘067D00⣀005200⠃000600⣊280600⣃004D00⢘001C00⡃280600⣋000600⡒117D00⢂004700⡀004D00⢀284C00⡄000600⣊000600⡃0B5200⣐280600⡃000600⣃285200⢘004D00⡂000B00⣃000600⣓001C00⢸005200⣃000B00⣂285200⣘001600⠇005200⠐004700⠂000600⣓000600⡙0BA900⢚0B7D00⣃280B00⣣000600⠐000600⡀002800⡂000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⠄002800⠦000100⠄000600⢦067D00⠤0B7D00⠤000B00⠣000600⡲280600⠴000600⠱000600⠢000600⠢004700⠉000600⠦000600⠢000600⡢001600⠈281600⠁000600⠦000600⠕001100⠈004700⠉001700⠉000B00⠵000600⠔000600⠔001700⠈280600⠔000600⠦001700⠈284C00⠉000C00⠁000600⠢000600⠢004700⠈051C00⠉004100⠁000600⠪000600⠢000600⠧280600⠣000600⠦007700⠸16A900⠤004D00⠄000600⠤000600⠠002800⠠002800⠁000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⢀000600⢀000600⢐000C00⢀065200⣀1CA900⣈007800⡃000600⣜280600⣪005200⢠284D00⡞005200⠙284C00⠛000600⣏004D00⢀067D00⡞007D00⠛067D00⢣285200⡀050B00⣝117D00⣑007D00⣄0B7D00⣘280B00⣍000600⣇067D00⢸06A900⣋004700⡁000600⣍285200⢘284C00⡇280600⣝003C00⢸297D00⡟004C00⠛007D00⣄280600⢑004D00⢀285200⡜005200⠙282100⢳004C00⡄280600⣏001100⠈11A900⢉11A900⣁281100⡄000600⡄000100⠂000100⡀002800⠂000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⢢000100⢀000600⢂281100⠰064D00⠴0B4D00⡶000B00⠳280600⠶280600⡶001100⠘007D00⠶004C00⠤005200⠶280B00⢾001100⠘007D00⠶004D00⠤007D00⠶281100⠣280B00⣶064D00⠶051700⠘067D00⠶280B00⣷280B00⣶064D00⠰004100⠇050B00⣷280B00⣶064C00⠰051C00⠇280600⡶001100⠸067800⠦004D00⠴004D00⠇000600⣲001100⠘005200⠶001C00⠤005200⠶280C00⠃000600⢖000600⠧281C00⠸112100⠷281700⡦000600⡂000600⠂002800⠆002800⠂000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⡨002800⡭000600⢅000600⢦064D00⢨17A900⣭28A900⡅280B00⣼280B00⣽007D00⢠00A900⣠007D00⣀007D00⣄007D00⣤287D00⣤287D00⣤287D00⣤287D00⣤287D00⣤287D00⣤287D00⣤067D00⣀287D00⣤287D00⣤287D00⣤287D00⣤28A900⣀287D00⣤287D00⣤287D00⣠287D00⣤287D00⣤284D00⣤287D00⣤284D00⣄004D00⣄284D00⣄004D00⣤005200⣄007D00⣄004D00⣤007D00⣄005200⣠280B00⡭050600⡯001100⠈0C5200⢩0B5200⣭281100⡅000600⢈000600⠈002800⡌000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⢐000600⠐000100⡈000600⢆280B00⣓06A900⣲16D400⡒001100⠃280B00⣷050B00⣷000C00⡘06A900⠳11A900⡒117D00⡒117D00⢒117D00⢒16A900⠒16A900⡒16A900⡒16A900⠒16A900⢒11A900⢒117D00⡒16A900⢒16A900⠒16A900⢒16A900⢒16A900⠒16A900⢒16A900⠒16A900⡒117D00⠒11A900⠒117D00⠒117D00⠒115200⡒114D00⡒105200⣒114D00⡒0B5200⣒114C00⢒107D00⠒0C5200⡒004700⠃280600⣺280B00⡲000600⣃00A900⢐117D00⡒281C00⣆000600⣒000600⢐000100⠐002800⠠000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⠬000600⠔000600⠥000600⣭284D00⢨1CD400⢭29D400⡍000B00⣽280C00⢭060B00⠭060B00⡭000C00⣭06A900⠹16A900⡭16A900⠭16A900⡭16A900⢭1CA900⡭16A900⠭16A900⠭16A900⠭16A900⢭16A900⠭16A900⢭16A900⢭16A900⠭16A900⠭16A900⠭16A900⠭16A900⠭11A900⠭11A900⠭11A900⠭11A900⠭11A900⠭117D00⠭0B7D00⣭117D00⠭117D00⠭117D00⠭0B4D00⣭067D00⡭007800⠁280B00⣭280B00⣭280B00⣭280B00⢽004100⠈17A900⢭0BA900⣥000B00⡭000600⠤000600⠠000100⠁002800⢀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⡘000600⡀000600⡣280B00⣒281C00⣐0CD400⣐47D400⡒004100⣓061100⣒281100⣚060C00⢐281100⣒281100⣒051100⣓067700⠘16A900⢒1CA900⣒1CA900⣒17A900⣛1CA900⣒17A900⣒16A900⣒16A900⣒17A900⣒16A900⣒16A900⣓16A900⣒16A900⣒11A900⢒16A900⣒16A900⣒11A900⣒11A900⣒117D00⣒117D00⣒117D00⣒117D00⣒0C7D00⣒11A900⣒117D00⣒11A900⣒28A900⠛280B00⣳280B00⣳280B00⣓280B00⣗280B00⣳280B00⣻280C00⣀287D00⢘17A900⣒287800⡂280B00⣇000600⣃000600⢂000500⡀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⠠000600⠠000600⠨280600⠮281100⠤0B7D00⢤4CFF00⠤06D400⡇11FF00⠠06D400⣤061600⡬061600⠤061100⠥061100⠬061100⠬061100⠬067200⠈06A900⠽16A900⠭16A900⠭16A900⠭16A900⠭17A900⠭17A900⠭16A900⠭17A900⠭41A900⠭3CA900⠭107D00⠯0B2100⠭0CA900⠭16A900⠬16A900⠭16A900⠭16A900⠭16A900⠬16A900⠭167D00⠭11A900⠥117D00⠭117D00⠬287700⠏050B00⣽280B00⢯280B00⡯280B00⡯050B00⣯280B00⡿004700⢠06A900⠼067200⡌16D400⠭11D400⠤281600⠤280600⠤000600⠅000100⠄002800⠄000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⣘000600⣈000600⣚280B00⣻054700⢚17D400⣘21D700⣛0BA900⣁4CD400⣓21D400⣛6DD400⣓0BD400⣀061700⣛061700⣓061600⣓061100⣓291100⣛061100⣚28A400⠛16A900⣛16A900⣛16A900⣛16A900⣛16A900⣛16A900⣛16A900⣛17A900⣂16A900⣀0BA900⣀0B4D00⣛11A900⣀16A900⣀16A900⣚16A900⣛16A900⣛16A900⣛11A900⣛11A900⣛11A900⣛0B7D00⡛284700⠃060B00⣓280B00⣻280B00⣻280B00⣻060C00⢀284D00⢀06D400⣐11A900⣛11A900⣛11D400⣃29A900⢸1CD400⣛067D00⣂280B00⣏000600⣃000600⢑002800⢑000000⠀000000⠀000000⠀000000⠀000000⠀002800⠠002800⠲000600⠦280600⡺281100⠴287D00⢴4CD400⠤0BD400⠇11D400⢼4CA900⠶4CD400⠤4CA900⠴4CA900⠾0BD400⣶0B4C00⡄061600⠶061100⠦061100⠦291100⠴061100⠤284700⠹11A900⠶16A900⠤16A900⠤16A900⠦16A900⠤17A900⠦16A900⠴16A900⠤117D00⠦0C7800⠴117D00⠶117D00⠴16A900⠴16A900⠤167D00⠴16A900⠤167D00⠦117D00⠦287800⠏281100⠵060B00⠴280B00⢿280B00⢿060B00⠴000C00⠖285200⢰0C7D00⠶167D00⠶16A900⠴167D00⠦16A900⠶064700⡮117D00⠿0B7D00⠧280C00⠦280600⠦000600⠢000600⠠002800⠂000000⠀000000⠀000000⠀000000⠀002800⢬000600⢡050600⣹050C00⣈061C00⣉1CD400⣸4CD400⣉16A900⣉7CD400⣉51D400⣉4CD400⣉77D400⣉77D400⣉51D400⣉51D400⣉11D400⣄0B7D00⡀0B1700⣍0B1100⣩0B1100⣉0B1100⣍067D00⠈11A900⢩47A900⣉47A900⣉47A900⣉47A900⣉47A900⣉47A900⣉17A900⣉1CA900⣉1CA900⣉17A900⣉47A900⣉1CA900⣉42A900⣉47A900⣉1CA900⣉28A900⠋061100⡁061100⡁060C00⢀060C00⢁060C00⠁06A900⣀11A900⣜47A900⣉1CA900⣉47A900⣉47A900⣉47A900⣉47D400⣉11D400⣁11A900⢉47A900⣉067D00⣁050B00⣯280600⣋000100⢌000500⠂002800⢀000000⠀000000⠀002800⠐000100⠐000600⡒280600⡶281100⢲067800⢲4DA900⠖11D400⠗0BA900⣾4CA900⠶4CA900⠶7CA900⠖4CA900⠶16A900⠺4CA900⠶21D400⠶1CA900⠶16D400⠶067D00⣆061600⠶061100⠲061100⠒061100⠒061100⠆06A900⠲17A900⠖16A900⠖17A900⠖177D00⠖16A900⠖16A900⠶167D00⠶167D00⠖16A900⠖16A900⠖16A900⠖16A900⠖0B7D00⠶061700⠃281100⠶280C00⠶060B00⠶280C00⠶064200⢀287D00⡶17A900⠲16A900⠲16A900⠖16A900⠶0BA900⠲1CA900⠶17A900⢶1CA900⠶1CD400⠶064C00⡖17D400⠶0BA900⡶290C00⠄280600⡖000600⠆000600⠂002800⠂000000⠀000000⠀002800⡡000600⡡000600⢭060B00⡨064700⢨17D400⣨78D400⡭16A900⢭4DD400⣭4CD400⢭4CD400⠭4CD400⠍17D400⡍16A900⣭16D400⢍47D400⠭4CD400⠍1CD400⣭1CD400⣭16A900⣧06A900⣄061700⢭061100⡭061600⡭061100⣭06D400⠉16A900⢭16A900⣭16A900⣭16D400⣭16A900⣭1BA900⣭17A900⣭16A900⣭16A900⣭16A900⣭0BA900⡍294700⠁061100⡉291100⡡061100⣁061100⢉287D00⣤16A900⣬16A900⣭16A900⣭17D400⠭16D400⠉11D400⠉105200⣭17D400⠉47D400⠩4CD400⢩4DD400⣩0BD400⣏11D700⢹1CD400⣍067800⣅061100⡀280600⡭000600⠌000100⠄002800⠄002800⠐000600⠐000600⡘280B00⣲061600⠐0B7D00⢲47D400⣖16D400⡓10D400⣲4CD400⠒4CD400⠒4CD400⠒17D400⡒16D400⣒16A900⡒16D400⣒17D400⡒1CD400⣒1CD400⠒1CD400⣒1CD400⢒1CD400⢒06D400⣖067200⡒061600⣒061600⣒281600⢒064700⠘0CA900⠒16A900⢒16A900⢒16A900⢒16A900⠒16A900⢒16A900⠒16A900⡒06A900⠒061100⠒061100⠒061100⠒061100⠒287700⣀0BD400⢒16A900⢒16A900⡒16A900⠒16A900⢒16D400⠒11D400⣒11A900⡖117D00⠒11D400⣖16D400⣒4CD400⠒4CD400⠒47D400⣒0B5200⡚21D700⠒0BD400⣖281700⣂280B00⣗000600⢓000100⠐002800⡐002800⠬000600⠌000600⡬280B00⣯061700⠬11D700⢬51D400⠭117D00⠭4CD400⠭4CD400⠭4CD400⠭4CD400⠭4CD400⠭4CD400⠭1CD400⠭51D400⠭4CD400⠭4CD400⠭4CD400⠭21D400⠭1CD400⠭1CD400⡭1CD400⠭6DD400⠥06D400⠤061600⠭061600⠭051100⣭284700⠬06A900⠹0B7D00⠯0B7D00⠭0BA900⠩0B7D00⠽067D00⠽287D00⠉061100⠬051100⠭291100⠭281C00⠩0BA900⠤16A900⠭16A900⠭16A900⢭1BD400⠭1CA900⠭1CD400⠭1CA900⠭1CD400⠭47D400⠭17D400⠬4CD400⠭47D400⠭4CD400⠭4CD400⠭4CD400⠭0BD400⡥11D400⠽1CD400⠭287800⡥051100⡅280600⠥000600⠅000100⠄002800⣚000600⣊280600⣺281100⣒28A900⢐77D400⣒16D400⡗11D400⣲4CD400⣒4CD400⣒4CD400⣒4CD400⣒4CD400⣒4DD400⣒4CD400⣒21D400⣓17D400⣓41D400⣛1CA900⣛1CD400⣚1CD400⣒1CD400⣐11A900⣖11D400⣒11A900⣲0CA900⣒06D400⣀281100⣛061100⣒061100⡂28A900⠙16A900⢒17A900⣒06A900⡛281600⠃051100⣚061100⡒287700⢀06A900⣰11D400⣒0CD400⣐0BA900⣒0C7D00⣢16D400⣀11D400⣀117800⣛11A900⣛11D400⣛11D400⡚17D400⠒4CD400⢒1CD400⣒1CD400⣒4CD400⣒4CD400⣒1CD400⣛47D400⣒0B7800⣚4CD400⣒0BD400⣂061600⡀280600⣓000600⣃000100⠁000600⠠000600⡢280B00⡯061700⠤11D700⢴4CD400⠤117D00⠥4CD400⠤47D400⠬16D400⠭16D400⠿1CD400⠭17D400⠤1CD400⠤11D400⠿1CD400⠭42D400⠤1CD400⠬1CD400⠬16D400⠭11A900⠯41D400⠤1CD400⠭17D400⠤1CD400⠤1CD400⠬17A900⠬06D400⠦281700⡄280C00⠥050C00⠥287700⠈004C00⠏003600⡽060B00⠬280C00⠭287D00⢠11A900⠬16A900⠬16A900⠤16A900⠤16A900⠬11A900⠤11A900⠬0BA900⠯11D400⠭11D400⠤11D400⠤11A900⠭0C7D00⠵0CD400⠤16D400⠤11A900⠭167D00⠽0CA900⠿11D400⠭17D400⠬0BD400⡥0BD400⠽47D400⠤064D00⠄280B00⡯000600⠦000600⠂000600⣈050600⣺281100⣙0BA900⣀4DD700⣛17D700⡋0BA900⣛16D400⣉47D400⣁72D400⣀4DD400⣐1CA900⣓17D400⣁72D400⣒4CD400⣐47D400⣒47D400⣒47D400⣒72D400⣐11A900⣛1CD400⣒47D400⣒1CA900⣀1CD400⣀16A900⣀11A900⣀11A900⣈10A900⣉367D00⣉064C00⣄280B00⣻280B00⣻280B00⣛050B00⣻281700⢠0B7800⣌37A900⣉0BA900⣉11A900⣀11A900⣀16A900⣀16A900⣀16A900⣀17A900⣀11A900⣂0B7D00⣐42A900⣐42D400⣀17A900⣒42D400⣐47D400⣒42D400⣀11A900⣐1CA900⣒47D400⣀1CD400⣀16D400⣈0BD400⣋067800⣙21D400⣛11D400⣃064C00⡀280600⣙000600⡐000600⠰280B00⡺281700⢴0BA900⢾1CA900⠷0B7800⠧1CD400⠾4CA900⠶4CD400⠤11A900⠿16A900⢤17A900⢶4CA900⠶4CA900⠶4CA900⠶4CA900⠶4CA900⠶47A900⠶17A900⠦0BA900⠶1CA900⠶1CA900⠶11A900⠶06A900⠷0B7700⠿114700⢋0B4D00⡥0B7D00⠤287D00⠶281700⠂280B00⢾280B00⠾000B00⠾280B00⠷280B00⡾007800⠲067D00⠦0B7800⡤0B4700⣍0B4C00⠚067800⠾06A900⠶117D00⠶167D00⠦0CA900⠦064700⠴16A900⠶16A900⠶16A900⠶1BA900⠴1CA900⠶1CA900⠦21A900⠶0BD400⠦0B7D00⠿16A900⠶17A900⠶4CD400⠤06A900⡆0BD400⢸16A900⠶064C00⠆280600⡶000600⠢280600⣩061000⢨0B7800⢩4CD400⣉47D400⡉11A900⣹4CD400⣉7CD400⣉17D400⣍16A900⣩4DD400⣉4CD400⣉4CD400⣉4CD400⣉77D400⣉77D400⣉7CD400⣉4CD400⣉1CD400⡍11A900⣉11A900⡉11A900⣥1CA900⣄47A900⣉1CA900⣉42A900⣉1CA900⡉06A900⠋061100⡁060B00⣩280B00⣟00A900⣠0BA900⣤284700⡀060B00⣉060B00⣅285200⠈0B7D00⢫16A900⣉17A900⣉47A900⣉41A900⣀0CA900⣤0B7D00⡍0B7800⣁117D00⡈1CA900⠉1CA900⣍1CA900⣉1CA900⣩47D400⣉4CD400⣉4CD400⣉47D400⣉17D400⣁11A900⡉47D400⢉4CD400⣉1CD400⣍0BA900⢈47D400⣉11A900⣁281100⡇280600⡍280600⠲281100⠶06A900⢸21D400⠲0B7D00⠗11D400⣾21A900⢶47D400⠖107D00⠲17A900⢲4CD400⠒4CA900⢶21D400⡶17D400⠒1CD400⠒47A900⠒16A900⣶11A900⣶1CD400⠖1CA900⠲0CA900⡶067800⣦0B1C00⡛06A900⠓067D00⠶06A900⠖281100⡳060B00⠶280C00⢲281100⢂007800⣴117D00⠒117D00⠒0B7D00⣶004700⣄280B00⢷280C00⢖050B00⣾064D00⠐067D00⠶287D00⠖064D00⠊064700⣀28A400⣴11A900⠖11A900⠶0BA900⣶0B7D00⣶114800⣶115200⡒11A900⠒11A900⠶1CA900⠲1CA900⠒1CA900⠲067D00⡖0B7D00⠺21D400⠖21D400⠲287700⡆0BA900⢲11A900⣶051600⡆280600⠖280B00⢩061100⠨42D400⢭4CD400⠭06A900⣌11FF00⠩21D400⣭1CD400⣭0B7D00⣭16D400⠭17D400⠉11D400⣩16A900⣭4CD400⡭4CD400⢭21D400⣭21D400⢭4CD400⠭21D400⣭17A900⣭16A900⣭1CD400⠭17A900⣭0BA900⡥051100⣭061100⡁061100⡁060C00⡡281100⣭067D00⠈0BA900⠉0BA900⠉0BA900⠉0BA900⠉0BA900⠉281100⣯061100⠁280C00⣭060C00⢁280C00⣭064700⠸16A900⣬11A900⣭16A900⣭16A900⣭1CA900⣭1CA900⣭1CD400⡭1CD400⡭16D400⣭1CA900⣭11A900⣤0B7D00⣭0BA900⡍11D400⠉067D00⠍0BA900⢨1CD400⣭0CD400⡭06D400⢉06A900⣬1CD400⡭067D00⡅000B00⡭000600⠂280B00⡳281100⠺06D400⠛1CD400⢒0BD400⣷0CA900⣌067D00⢛0CA900⣔0B5200⣒41D700⢒4CD400⡒4CD400⡒4CD400⡒1CD400⣒47D400⢒1CD400⣒1CD400⢒1CD400⡒1CD400⡒17A900⣒11D400⡒017D00⠃061100⡒061100⠒061100⠐281100⣒057800⣐11A900⣒11A900⣒117D00⣒117D00⡒16A900⣒11A900⣒16A900⣒16A900⣒06A900⣒287200⡐060C00⡀061100⠂281100⣒281100⣚067D00⠳16A900⣒16A900⡒16A900⡒16A900⣒16A900⣒16A900⣒16A900⣒1CD400⢒1CD400⡒1CD400⣒17A900⡒06A900⠒0BA400⢀0B7D00⡀284D00⢛06A900⣴1CD400⣒11D400⠒065200⠃280600⡒000600⡂000100⠈000600⠌000B00⠭281100⠩067200⠈0BD400⠩42D400⠭17D400⠥06D400⡍0BD400⠧067D00⣭11D400⠩11D400⠽21D400⠭21D400⠭1CD400⠭1CD400⠭17A900⠭1BA900⠭1BA900⠭06A900⠏287200⠩051100⢭060C00⠅050C00⡭281100⢭06A900⣤117D00⠭11A900⠭11A900⠭117D00⠭16A900⠭16A900⠭16A900⠭16A900⠭16A900⠭16A900⠭16A900⠥284C00⡄061100⠨290C00⠈060B00⠭060C00⠄287D00⠉0BA900⠭117D00⠭11A900⠭16A900⠭16A900⠭16A900⠭17A900⠭41A900⠭06A900⠏06A400⠁28D400⡤28A900⠏28A900⡤1CD400⠬11A900⠭28A300⠍004700⠉280600⠍000600⠅000100⠄002800⠈002800⢊000600⡑000600⡑000B00⣚280C00⠙061700⠛06A900⠛16A900⣛0BA900⣓01D400⣘11D400⢲0BD400⣀067D00⣛0BA900⠛17D400⠒1CA900⣚16A900⣚0BA900⡛284C00⠃281100⣚280C00⣛280C00⣚060C00⠐287D00⢀0BA900⣚11A900⣓11A900⣓11A900⣒11A900⣒11A900⣒11A900⣒11A900⣚11A900⣒16A900⣓11A900⣒11A900⣓11A900⣒16A900⣓06A900⣂001100⣓060B00⣓280B00⣻280B00⣟280C00⡂007D00⠛11A900⢒117D00⣒114D00⡛0BA900⠚287700⠛01A900⣀0B7D00⣰28A900⢛067D00⣐117D00⣚0B7D00⡚064C00⠛280B00⣛000B00⢛000600⠘000100⢈000100⠈002800⠐000000⠀000000⠀002800⠠002800⠱000100⠐000600⠌000600⠭280B00⠻287200⠩287D00⠿16D400⠬06A900⣦067D00⡝28D400⠿0CA900⠦06A900⣤286D00⡬287700⠉060B00⠵060B00⠭280B00⡿060B00⠥280B00⠿284C00⣠117D00⠤117D00⠤167D00⠤117D00⠤16A900⠤16A900⠤11A900⠤117D00⠤167D00⠤11A900⠤117D00⠤16A900⠤16A900⠤117D00⠤16A900⠤117D00⠬287D00⢦281100⡄280B00⡯280B00⡯280B00⢯280B00⠯284700⠈284700⠉281600⣠067D00⠤0B7D00⠶007700⠟005200⠡0CA900⠤06A900⠯284700⠏001100⠍000600⠄000600⠅000100⠐002800⠡000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⠁000600⠁000600⠑000600⡉000600⡫280B00⣛067D00⠈06A900⠛16A900⣙067D00⣆28A900⡙00A900⠋061100⠁060C00⠈280B00⣻280B00⣻060B00⣊061100⢀067D00⣀284C00⣉287D00⠉067D00⠙06A900⠛0BA900⠛0C7D00⠛0BA400⠛0C7D00⠛0C7D00⠛117D00⠛0C7D00⠛0B7800⣛0C7D00⠛0B7D00⠛067D00⡛067D00⠛287D00⠛284D00⣉064C00⣁052200⡀050B00⣟280B00⣏050600⣻280B00⣹280B00⣛001C00⠈004D00⠛287D00⣁0C5200⣚104D00⣛295200⠋281700⠃000600⢑000600⠈000100⠐002800⠑000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⠠002800⠂000600⠂000600⠊000600⠲281100⠑281600⠞064C00⠳115200⠶284D00⢦001100⡀280B00⠾280B00⡿280B00⡷281100⢠287800⠶117800⠶117800⠶117800⠶117800⠶107800⠶0B4D00⠶0B7800⠶0B4D00⠶0B4D00⠶067800⠶0B7800⠶0B7800⡶0B7800⠶0B7800⠶0B7800⠶0B4D00⠶0B4D00⠶117800⠶117800⠶114700⠶0C4C00⠶001C00⡆280B00⠷050600⢷280600⠲000600⠗001700⣠004D00⠶064C00⠷281700⠟000B00⠯000600⠒002800⠜000100⠄002800⠂002800⠂000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⠁000100⠈000500⠁000600⢉000B00⢈000C00⠙285200⠉065200⢩165200⣁005200⣤281100⡉285200⢠167D00⣌167D00⣉167D00⣉16A900⣉16A900⣉167D00⣉167D00⣉167D00⣉167D00⣉16A900⣉16A900⣉16A900⣉16A900⣉16A900⣉17A900⣉167D00⣉167D00⣉167D00⣉167D00⣉167D00⣉167D00⣉115200⣍115200⣩112700⣉285200⣄280B00⡝005200⢠067D00⣼167D00⡉065200⠋281C00⠋000600⠈000600⡀000100⠁002800⠁002800⠂000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⠠002800⠐002800⠪000600⠂000600⠳281C00⠘067D00⠲0B7D00⠒004700⣆007D00⠲117D00⠒117D00⠒114D00⠒117D00⠒117D00⠒117D00⠒117D00⠒11A900⠒11A900⠒11A900⠒11A900⠒11A900⠒11A900⠒117D00⠒117D00⠒117D00⠒117D00⠒117D00⠒11A900⠒117D00⠒0C7D00⠒117D00⠒007D00⠖001700⣃002200⣶0C2100⠒281C00⠗001100⠓002800⡚002800⠖000100⠂000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⠈002800⠈000600⠈000600⠈000600⠡001100⠉067D00⠉365200⢭0C5200⣥284D00⣌067D00⠩0B5200⣭0C7D00⢭115200⣭115200⢭105200⣭117D00⠭117D00⠭11A900⢭11A900⠭117D00⠭117D00⠭0C7D00⡭117D00⠭117D00⠭0B7D00⣭117D00⠭117D00⠭117D00⠭067D00⡭007D00⠉284D00⣤0B4D00⣭0B2100⠍001C00⠉000600⠩002800⠩000600⠈002800⠈000000⠀002800⠁000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⠂000100⠂000100⠂000600⠊000B00⡺064C00⠐0B2200⢒065200⣒004D00⣀065200⠒117D00⢒117D00⣒117D00⣒0B5200⣒0C5200⣒0C4D00⣒107D00⣒117D00⣒11A900⣒117D00⣒117D00⠒117D00⣒117D00⣒117D00⣒067D00⡲005200⢃284C00⣰117D00⡒285200⠒001600⠓000600⢂000600⠂000100⠂002800⠐000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⠂000600⠡000600⠠000B00⠈001100⠈064C00⠩0C5200⠭065200⠤004700⡌284200⠩284700⠉284700⠉064700⠉064700⠉064C00⠉067800⠉067700⠉054800⠉282100⠉284700⠉284700⠉284100⠉285200⠤0B5200⠬065200⠍004700⠉000B00⠅000600⠅000100⠄002800⠈002800⠈000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⠐002800⠈002800⠚000100⠂000600⠊284100⠘064700⢛0B1C00⣛052100⣃002200⡐012100⢲0B5200⣒064D00⣒0B5200⣒065200⣒064D00⣒0B4D00⣒0B5200⡒002100⢒285200⣀0B2100⣚0B1C00⡓281700⠛000600⠉000100⠐000500⠂002800⠊000000⠀002800⠂000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⠈002800⠠000100⠈000500⠈000B00⠌001100⠹0B1700⠽061C00⠿281C00⠦004200⡙002100⠿062200⠦0B5200⠶115200⠤004C00⠟001C00⠡281C00⠾061C00⠿004100⠯000B00⠍002900⡵000100⠄000100⠂000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⠂000100⠂000600⠁001100⠈281100⠻0B1C00⠙101C00⣛001C00⣆281C00⡙284D00⠛005200⣡064D00⣜0B2200⣁291700⡛004700⠁000600⠁000100⢀002800⠐000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⠠002800⠐002800⠔000B00⠺284100⠰291C00⠶061C00⠶001700⠶0B1C00⠶281600⠟001100⠋000600⠁002800⠰000000⠀002800⠄000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀002800⠈002800⠈002800⡉000600⠁001600⠉001700⠉291C00⠝001600⠋000600⠉000600⠁002800⢉002800⠈000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀000000⠀]]) local width, height, scale, scaleMod = image.getWidth(source), image.getHeight(source), 1, 0.1 local targetMinimalScale, scaledWidth = 2 / width, width -local bufferWidth, bufferHeight = buffer.getResolution() +local bufferWidth, bufferHeight = screen.getResolution() while true do local transformed = image.transform(source, math.ceil(width * scale), height) - buffer.clear(0x0) - buffer.drawImage(math.floor(bufferWidth / 2 - image.getWidth(transformed) / 2), math.floor(bufferHeight / 2 - image.getHeight(transformed) / 2), transformed) - buffer.drawChanges() + screen.clear(0x0) + screen.drawImage(math.floor(bufferWidth / 2 - image.getWidth(transformed) / 2), math.floor(bufferHeight / 2 - image.getHeight(transformed) / 2), transformed) + screen.update() scale = scale - scaleMod if scale < targetMinimalScale then diff --git a/Wallpapers/Catniss.pic b/Wallpapers/Catniss.pic deleted file mode 100644 index bb0c4024aa11da49ffe802929bd726110fa4552e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39107 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 diff --git a/Wallpapers/ChristmasTree.pic b/Wallpapers/ChristmasTree.pic deleted file mode 100644 index 62efb20fb1db23c1905e083b9c7464ddf46d2599..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9843 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 a&nY|FgPe-1+w^TI6~twm+laX1p8Y>FkB-m) diff --git a/Wallpapers/Field.pic b/Wallpapers/Field.pic deleted file mode 100644 index 49e3905fc65a4b969a307be2c1dfef2295a8abbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39749 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 NJ4hs#n8%_B{eREG3bX(K diff --git a/Wallpapers/HilbertCurve.pic b/Wallpapers/HilbertCurve.pic deleted file mode 100644 index f7bfbb909725ab6e9402c81491305c19153d5e4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8735 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 diff --git a/Wallpapers/MoonTouch.pic b/Wallpapers/MoonTouch.pic deleted file mode 100644 index cf3232621a49c04494565a27ddab4bb17a36f8b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14131 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
vl4Eutw~oD19MBt3`3#?F^5Ihf+j;(XT3t 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 diff --git a/Wallpapers/Nocturnal.pic b/Wallpapers/Nocturnal.pic deleted file mode 100644 index 94555c332c5e79dc5887414db10c228129ee67f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10077 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%0D2&qI9-6b#=R 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 XAZ~>5eMFREZd`v)g&I|ubmIR3K0AE7 diff --git a/Wallpapers/Tatu.pic b/Wallpapers/Tatu.pic deleted file mode 100644 index c016ae8dfb9af7780ee8b8873910832882cb7a03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34079 zcmXWEXIooamnC|}+FLn+Ktd>@oFydN2;1PCvkf>Wz&1E=oU@I=Ie~3(4jBAaRac+$ zc2(zG-5;u-=XU)K{fztRj*0FU@By*+T64`X=9qJ?vwQv4O~&~ue*5kJtk!rf{h8awJ(WHMU0iOv8-?0eEC`pHFJmQ>h(K! zAH4YywcHzjF!^To{l`ykmlQ(@EKsnN6N&R=pN717R~zy2rwblKFi7j}K( z(bHEB!}ybDFQ=Tw`wu3czL<6y?u|cr{iex$_uhlaC(mBIZngwN{L!-)ucj+F=C*n| zcJnunpS^tDV)pt&yY5dsdC_Wa_68!c$4{PmjLueHAk^VCx?8>N!Hza~e9Gaf&riB1 z@CW>O_?zJ*D$ro)gtY=!9`=ILtYujG`Qxn_RF$K79A#`9)Mhm|Hu$ikglkj$$@OWi z6`RtyTf`k`Ren5a;+MD$Y3wo@s+I(Lllror<3Ce)k;d;C?c9a$R%C6+=J6~9i^bh(zt9>S$xjnp{B2{AcJ7^%wYH{TF^&pF(8XL;*u#74rMT&Rip1iXsT%@9I;@x3niPe6Cs5W zBl^;qGV^e8*{&sU)QtA5Xbz}{@w0N)9yzdGSwp2Mruo`;8B}_Q3FWj(EH%TDQ7cbc zur8~09sOxVPfp7p`)R|nydjt?RDTpyeo0BobUm*i6H%^hRh7qwGCVom0%Q@7fLeXO zfD%?5<(CtG;4jLmo;iz?_=7036#n9RonS9o& zJYkP&3VT&3>VwmPS57>0;bnz8)o`{(Wf(omL+%fuzpBhUxXx|_;p@W`mF31P{+SC{ z-;Y^p-GQ26NQH32h2tYy0Jbn*L@?fMY{Hoiybt2{kQzD@R`+Kj+V>wnMNi&-4yobu z5jA$Z*W|&VAFcq-2cd;w8>aY!9Tn90Z|@8}N&)NpRE^TS zJPrr;^mAt^+^wOPoAvfkS%Y}YccTUU_4&sxYNo9d+W=Kj_U5Ycen~=0L8YN(;mFY+ zX;w#b5xN|GTSj#%qq(BYwn0ri(3NREynmdZzFER;YNA!cxi|_bbeeEDt-ky;Yk^e1 zRXK7YbU3v_i5BlO**nl4$EpNQ8^p=XnXo^tPLG%|ki|y_a$!VWs%v@8V!&_`^G2LX zX_v0QPUD4HwfStwxv)BdsLiBdNXK!L3MA7wV@58Eu?YNj)r6WAE)&dLV@F1TT4OQl z6$Gk?mf$VpOV(U*=?EuOAZ$=ME;wz{Y>usI)sfApY@u=F~Dzr_-n=?<-lkJ6C=8&p1sjA zVMoA;{2@zy}B7 zVQup59MwtFF>S(EC(hUBpE!O~FjUpHk8v1Nux6FjnTIpOL$Hdz6!E+0%k2l;^9E2J zSyYV- z&L`o?s8&xN?y~y$HH(TdhL9DjbNHIq9?yI$A{B;~!%>Q3FTDpJaI6*X3cn|DL%hP( zDvc$GRpJ5C*htf-FsH;?N4n_?PWI6DuJzJjM?4s6N3w?&*n-o&Fkog3ZL<5PjNUbF zV{#DV)Ql^A7^Hl68tKxb=*VHN%BUBXmwgs%4B=`<`)7Xeu~CPLuc%CpB~Fl^d$^sDUUEVa29BH_Q&-MA|^a-8(1YpQP<(0u) zfU&5Iu99Z5y66F5D#Jdg&(g}ak$E_BxPlsa`Sq!~>1FcM`1O?nDT)7t8n?=oukOIAB`?wL(bs+EIj%w;iK zz&(jYr&8+Kj}WY;#iJ@`XwMZ4b@5A((G#!43P-c%3I2Ll#TtdTf)0fvF?bSm0@{i- zyYu*7)GEs+OIQ|xql*h~f5tCob6QT7--VTJcL}GD5{R9RVRsh$^5_d=Pl1ZNa*CfD zq50DE?zX{Z!(@)|9gH(VEvs9p60$KtzRm~5#6KBG>K1_XiF-izA)U5|`CWKpIyoq3r zrm5YZm<{1{7#AYA*=uxTxJBYkM*!xK8hsST;~v#b?K>x`GIf*+S;FS}{0qx(_`j4A zz0RLJ^ZgR&+}PfQD^#(gMr}aL;8r`f6>(#V(BkeiReyU8>y#hP0E`(fw}KlZu&I6h z5BMhoSwis&E>&U2s(xzTo&o-}PXv39pTgc8-?B>-`c#guYRAU<{7Z2X1U}9poJM}o zEdj3`dxkK>u(;1i*cL^p#_01Y1Y;N(6Q2M; z;&dMG!`PNX7fpDq!XTIvPuGkkeUy3t8*2Ix75>!-?ctJy{nNdK0;V9&_h5`m=#a4J z!=4BD%umpsUeTWZq=5Ev&>39l{KH^d*dGsIfteqyctTzP*j>l zAgspdpyC*^BO^ZW;vg5-j(+i;6)f#nL$vTa)7D4ez&-{UAyYD;_cVV=W>QAY7OflOz{DE$ld z`6(K{_%yAC9v-VTL!U^jFNYY!M16jm$2QJitOq}qVkSgr6Gb+MPvV|W)M$HcSjoSl zcsuj(m(fFgqO!aYlTKrwxWX2ktIxkNhyvKLyB`xg&h<6=-*$NEyw%A;;%Hc~Da*9N ziuLvRw@tr6u#^+f6^vD_Y2VK@jRuA+VVg48@{+?E$}kNoL3)S9rCc%^t4wW{=s2C! zRva5isq?>^a3GDR8T_7Aw`c62`yFzjC!$iBA?nw|no>BQ!hMsa1GyNM$FVGlYDyh_ zm)3?pe>Y3=#RzS%5mS#wQ8a0>9edOGk-;~MKr3$vEfK`+Je}VY7)s)e5hp`P3F7R; z5gsXXfp(~#RTZ4BVmJ*~8GS}ogu8@~Jj3P=v_#=iI1p38BXJe%Fuikg}n-z_7fA$6dq<@vb&ZIB?&I zyK)p|TxfXLhi<(sGV#1cd+_jOD|{Z+67y=!fp8mI?CRkY2Oc`r!zmY@5*sPM%B&2a zO>`uQO3frHe04-K;kpy2!niF6ezXH0nla=@a}XmD^?ZJm8Sg1SE(c%<;Q_(CHa__^ zj585@7WaC(1;Z9x31T>;uH6Z16VtN}x{4K_@qcLdhs4=BaG_V};403J^L(4IOR~Q$ z;!FbKOx`(vThutlm8LxNJ*~<#@MsFXVX_b^kgI7;JVyP*qiDt|Z~2 z&`nXarnO9NMMhwU6_c+{0^HI=|r5Y~PIks$N|j8MJfj6SsQry~^D!%+m3;Y<&sQ8X;M@k=o-^fEF2 z^euufG!fN`(*azcqlL6!s^L4g)#qn;aI-Wfy82no$U_Ljm*Hou*x7Ki!TS7rJ7WNC zwm-wgS}L@#B)&Fa(} zKH@e7Mw6fgzEJ3l!9tL(VO3h9MRSf6g;ue|5lSFq#P$@JG2|^MTcPJsDho=AW9?DK znsu_bCY%f6g9OeP4mQ%I!;PanQlI}IA&g2qAmPZFVxVL4o)y@CjH@?vAgaW!G8t82 z+#9NCahyuvXBLw-u9JQ|fj3;Nt%H6SEfwwl!K-n4RFc77VpsxhrxjQH!g^4nL%sBjr^fg(oyV$LaMy#U_4$vQQO(dXj?eLLS99gG z%okZ6ZA@~=PdsIb*~c?V{!x)Q_gIeFjQRTfXDij7Kbfzney(lj2qk=Ukg-0GgGQ~U zf=nfllhghE0k!7I2q9_&hciY=jL!TsU%0c64`0{MU+(RqmhV|fFBPCgkUQ8HhEiDX8VsuaNbWS&#jV8 z_=6r`X9HwyGipQJ$1w>fWdT<=Gn8osSEu-wSFSbS(T)pzun(<0wAOYs*XO@8lpSTL z+rlh@f@`ZXkWsa(H7$u}K>}(?C`S2#X`Tv+o^geghIKkNB$eB5lz{FR1M2BJY^U@C?dm5g`O{SFEB9 zwD>tj$r@#UZD8>yyK7v%6&EGTsNmK(9iR_=_4%Kq5F~8V*9d-MCNV+5@2OC(XO5~# z{%C_hn}PcLys#2Z+E!c=cchicl7XIO2B$Q89$J9}YDw9f$`YyU74%irt`kN=25*L# zill|Hz#yy7^7Wes`1s9J%t{8z>d@6`?%)b@6K4lMjf)Ajl>EY5BaWoB<3o&eSIpR# z!Fdas3K(=kxzy5?5qN4kFlI%@fxHVflRc_(M-?u_RC!e#t97yN?j+{Sh-5HfA;qcU z$q0E?tHS9R3OWW8Du2?5J!UjJpcQe?sk%uD`0Wfk)dYHyxN64k41~p9nS?EkCJD4r z_!ahR*r%&lGJ%vqc}hm*E}E5jNd}RumhSGcVz~{=^T-xeepeakPE~zI%a?@uNRGRl zEHFh+rB!-)7Ey*+2C94FI|6Fs9!6JdlNz6B#$&g^>kE%RZBf4Gt%!P*Kj_t3Jbt@2 z`Rt_=Ph7a)h$KC7*1%;!o^jk1(vy_xkgmBOTcL%&7+;m}7_=Z;wG>1e}$9io|7-?36BW|2(!ATvD&A4V0 zFLlkqXDRpD2%|K$QpYpuq1KEYZXEOoBl?+%)sFi7f}J{KsHkn9l~TkQ);w%wxQt9+ zTdFus*9=FV;1#b4#m)+iV#M(-n#GwKk@z|bT7x_uKL7lnnqu&9el`3Ot)rL%sJIK3HH4w_-yonU;vbda03UBPRC7v-M*r$d!CZYUf6kZI9OJ3QF zu=+4hj#cl(OjV55Q9_$Iz;i}|xMqdrRkh~64pRcnCe(7O#bZ+&HkI&|LCvONAc?RM zd9qzJ(JV4KtyFnq!^%8D1*|M$Vi_HCM4;A^0)7&r(|5fyd4Tx1~J8` z$M{a(`WnNd-lIr}(lI%)>h z)0>oRm2TUcqhl?=S5$AlmejlVWz1GEQPo#pe(;1@gw23}2~8Q5>bAg`MJbPu1#}gW zDPdOxbXm`Pv|;9yqr#lMG~tegyb>jUUfii$fZ|_ljfnZPvS?zrYFa|wcx}a@Aku=7 z^0?VY9~vVjXAn$cX%^ok1-&3R<3|!lf@l&r_(srDNRV6`j*E(Y`N<$*)=<^qG+?IR)iddy&SN&hUD89uutP$G2P1xU=Dgs%R@|@8{|(iCobmc`KcjdK z*Cpsj5bPq+9LH@FhWSG#ioJ9+?e+P;C5ja@IVw~eQ>wq?%0#mt*XRFU$|9y1={(i4 zQv|ZHWp`$@Jb5UXN^l67n8kr8u&vX7(K(m~8i%t=d3D&@D_GBN`a6q(kIzbW_ zld|#wfqYMRZdw}M8LZAyCWZ<*!(>clatW+3!jr-=6IvXoih>op2!o%8(JWFp^24qS z7!4s=QzLX(r-YxqDQD)xWomg7;ERfGIW%mHqghu=R%Ij+Xw6}F0W(Ey=EJ8F)|Bz4 zQ}sTqsICiDvH+^WQ$C(RGKD=kyf)IK(^4u0Ocn7`Od^XdJ^DH>e~Ktmi=MwQ#w>^+ z>QS$1S8;}?w=@&28E;$h%7aM}ng?5hI9Q+m)6gg@F_UJA7Hb4C%?L)aVx!Ox9oQ?t z%7;XK{;za2Mp8dZ>+}CWR3|`t+PFw-m4&?spk037iFx_M3NFghf>_@`r#I^J|47Fm z&;KCd`PK*@+lEW>*)TTQ__#R8^ZwgKzS@X0efY$vc21Nh#&WJ2;D6ovlfL$m@EvaG zJzUC%3=422JYkUoo0W?8R&m3CE+ctcw2I)O*jtg>Puz~>QW||=o8%Y(fa(qC*&3i;kHrzL7>Vg)Un7KQH&?*^Z$Xr9ijg@ z)W{`H$Rqr?E?Ljd`uu<784EkKne{t5zw7n+|3oa(&jg@f3UzMrnMryVo+2Ypmfs3k zCfQr7(6s-VS}64Yun`aVkZl?M!h)^B*GI9YKL1~|NiNBaZ*xrZ$o9y=Jk3#*MU2+x z|0^>tLA`552V!_)rwChT@GpGD5s4Tb2*!~cU?P3E#`I=M9A}bvCjn(JqxJFR&Kv19 zTkP0Y!g9%f*MFvW$zTH&h&Xy>0TVg(neL|VPkwm6ps;3`C49BuP<{TtQ9(wy=iNrF zp9_k0@%1*zdRTZq${+O0ZaXn7DiOkE2`e5vs?Yy-!zA;ic49V2J>sw^x}VwRqOT!& zSHb><2D4cDKd6Nx421W@e##i;xiY?O+$+lTdyl%pKeUaJw6+KEa-5IzV86VlO$9pY z^Z%0(NsOa7#zmFt^Z!KuSYymM5kfG6Wv0dV(#|C1xl*71U;5-?J~J*S8p9!}t+ZpG z7*zmI>hu5GH7WTF1C){9n{Y%jSU=38{D7Y{9`&SxGc#P-tsGCC{HE}JzmM_5KD12p zn;(ZNJm@G6&QQ>K9G1#a31eMUf+%h?8B{)a+tJhu*_maI1*|qid~nwnCR%?ZkzA2D zw5JS%yhf8G$WVo|r0J~FE?U?%)|L3=cCw9HTvfUfSZPOofLgV)Mpog30VnOs!m?_w z$r#5969&x6lgz05&%%gA&~DQ;YKI!VZiUr>ie05}#R~ffWzmMCCbfhj=^W;wBho2Q zLZ2AUVV14vb4?XeGD~&kj~dWwQr40gCJUBivC1kLUAk7#qQ!Dq%Pv_d+rAF&;c<{fM{RX%*h!wa6NntcM?s%jcdq*5!J2p!m)2xLj9=Z`|QPIb(s@3m-$BVW$ zNw{7(@!ExFQ}`!3q+7zO+hDIT+8;Dg4?6p3m`>~(r#0`N<#`mazl-dLO{$JZnA@mk z5C*&&F*T9E!;Asn?d(Evpw)?H7luYj+*zA(!U0dW-UNFK)`ajfqV9kEgHJprq4u$n zB~*q50*$LKxacdC8)uC8)XNC@!i1kkc}gj4l8%uUGG_~uYPeT1TWfo#NL`gAoK7jL(QcxRMBG+ zExDk8L{Or=Z;e#_EX~NALX<$OXI+-mm@?GJ#^=?N;Rx22k<8KPr$ve5C{g69Sv`N* zO8Qq^KoB1#+O=V8h|zB0p-VPU)u%sw{l>f|4ucLZ(vVyPnl__~fhpIyL2ZW8+grfR zGB(W8yX?r}vRLsUNv=AOsv@PHf3L9zreb8D626$3nOYEZA#WlccxXiv&r)K588My` z^`73%qQj6ubBRHK48asMcX4BsSxeOrxG?tkSr%<&e2~VZ1wE{&@aHErJRi|COt%0oZTQsbQ7TSS$E#MMTyFI6C1-odSl?*J zU;w9tWp2Yk2{BD%JqbnMh`VhiSF-TPAnaNMZQ`)nFwBs`%Gd*ir3STPRSvB-Y<38d z_^>PtWQDb*cnSB#9eNS3FFdyM4CrR4`Bfa6BB^$*h=YW;)W?Y#ekzXvF^UonNI5x* zp&7;k+NX;UhCguNsO0&t#94_AH zt`P>FGqXd7ySHGTyQinJ8o;DxG+)GzU8R}TLVH+V+?8StaN@XRY6fgT+D&Ct() zcD#z?6335c$YfLIlq~I!=fP!Bj(iONnsi4A+x{evu{DPygZd1i(xw~^%rXXSs7PUS z$7dew1}R*whx(J7Adg+7!W$|?w`qf6@4mi%f^j0{0&2sAN^FuV}MW%$-8}KeWU7CpUgr)G$Z?pidHQA1q=0 zzwjUr{y}Jc(2IkN>r#xJT-BFx0vHc= z3iMe2fZ^TE0^Y)NbHnL2Rnfndg17!1fiq zH?sZUbwxdTqp-57-F);k3$M5;1Bxl~cwin=w4gJKrB+d{WFDC!PL+_#G0=E{gAyex z;wwUU(yQzk3zEZMctO>)^Ymxy3op$=Fc|nwO~BcD5)Am=f|wn42^R-RGN>pv<<%AD z_5}7SmNua|i!Wig#mlr~NW4sxI7$U~$Al%Z>R4+)H3N51oqS8`Qa2!xlG?r9#&nx2 za~1H`MqLdQcup)m2+cG&%%eLo%8K0x`-~pc7hci4q>W%>1J~_pFwJl33$IyRu5r<$ zvcR|iMF+l0(w{*WA1L)J1HMtG`6j}IOqIHt7Y2JpN>$#IlQ7XhOTvo06avnG`$l{n zq;eT@`Rla9_F zCAFuo;=mKlu(x8=fv`lvR!MS>)EB0>C!xI~gM=Sx+~O;!)4S>m(~Lef)hxC7s|F&H z5`>Ypdqv0pbKzVUC!0IM#D}ZD5g*T?(7At!2??>%_Mh4!2F!_HnDK=wu+M};chh2 z8}=Mb1=xfr)XAt4()1T_d72Ns#Zb>(vvIguHN>S9el|sd7I$29sl;5xW>n(nG)S8A z(L|0<3-2W_5JlNWPrK62U~|cgP$L5TApWH5dsV?R4c#K9*G5w(R>A0M52eejHcVHw z-jT^TTuHQ~WMdUm9LXdSMr_I8xK*iTIV^Qa@3EpWk$no5kQFM%Nv51ll1jq`1I~)& zvxjtu9y%ucOj(rBNuS1UJ6+Ap8u3#C<83LvO@{GW=w4=t4!9%g)mtfDUUagSqDD<_ z7+P`0z&eDw_tDC}Tm0@sT==sw3G-eFhwoesQgB~G_vahBU&v#KIzv+oB#=(Brm4D) zW%Q-P?-=U87o^xztI~B5I}O*^W_FE%x!6{*&ocH&8jwUFDU=#lHN1@W~Pkg z73Duw)ll{&wNY_>7?R91%lbPTdqU)7D9W=f7&TzZOFdCHzX$Q&h4Z~M z_(?DON0bS7&7!oDsJ#^{^1EE(boA9gVc zT$K<-L5!GjMChfS2AM>>AYo{a(mc71F*Iqam8GN>1I$Kr+pxWeStmA@*d@wiu!1E) z4TS;=x-<_Xz6h{=(o4TFT3`4=N?)pg4`t&4+!l^0FL?JWjl9Mgh~8C=-+Zbse52lp z%UVn4!o~DU-Mm9SD^=*4p&7*(PABlzEWL zCJZ;!XbnsC9BIj9^D#29SZ7o#SLd;(gh3t(^?t9ewUx?AL{bQ3kS?&H5MC_KgUa7BQ+Wg2j_rypVuZX@tu2Bebq|;p5ujZcsWE zu7(J6g=j(%&n_(bz7KMo0lcj*%+UkPQSoM_F{Om4JSQMgK{j+!;--?a#xP$wCs~S@ zOx(gxlk@~;vB5|Vp`FY-f6I#eAC%%z0kkEoyTGUV;A=?vhv-YIK%IHX@VO3$IE{CW zjkc4D_{wb^$l;o_0cCNgz~r9Z+9OU-$9q99(Ozmk2W>c?!I}ZtSbjT| zVg&+krI+qBJ&9(}+v3UXR#je7lpZ)8`)rJgUWwlZX(bL}T%t@fhY%JPNF9RCmsTV& zVN6uojjiH+{U}OjPDOeUIz$sXyTHy&$-hQ2n2|*}BoMYD-$S!XQP|{{S#McgRih6z zY%^k6N_tm57V)!$?`6ECJJocpZ{UytJF_aWyGIT4k>5s16|e?9=#%>1^?d=+eZ9gnl`6mUuo|#SlZx2)~J>PgWiO>cEm3lR#pEgBG>_kVBl71qs0iW>Ouj zQ^dd?NvoI`C%9@#`NLJb&@@}m7G160%z?A+tfVt7C2ex*OIholn4_T?3|dE&`1`mS zR*RIAPZ^mXM9f%i5e!ORL~81rw@8bX4GumQH!L{Xzv%2AiA%F0Axw-P!4Zmkc$6My zxQC|u&?zG!txQg|QN0ys{WxpKB_}yTtr-pzK3H+RmvHU0105m?r>N^+96S!X-%J!y z16#eN*E(;4H;rW(b#%y%17V;`OW~(U3x>iL8YY=PF(h^|*o4WM8d-#GZ20S^Q#x$4 zcVaF|)|jGIfn~a?ZehtG1#cc}3u@=dB34CUE~|a>G$4ycVZ?N7rD>V<6>HY6vly4w zx|gq5nMEkC@m~dP_nY}5dKZH_#Uch2XIHLdQHa*e3O8eTpy6^{oxPC6Wh(*>mCm@B z9*QSiUghUQXz!y`GuYAyspF!wt=z;IWpGhM;chS?K?&i1LuWWc8x%g9KZYob+cou9dTgG9A15UH$m4j!WUojAi<^y3 z3;)VCL7!p6+;3b`3J^}9B&4mhATjZ+sVG>d9THzqz*BMXuO*%u@mQR2r!?BuNGrD5 zNdZzDgZMf{{L(3W+231!BLGOV>Z#JOm!zVer5S2cv@9ug`C39fd~SiyiZ%xc+*{D;(alLixwQQvb;#xEFxYiHtWP` z(L;7ZdvL-hJzUZg@g>esL%h({tyKH27+h75hTW{wgq6#khC2(lopDHe{qb`NubJ-| zqi8c|!7vTcYlbb06*+u2GP?Pb2;?R8E6QM3C$c?Cg47Rbu{+<6Q}u;^tx1m2iAj-( z9~T}_E!!k>`lCraax+>R?P3SGD^rxSY*=O>)IobnEkBt?)sJ0OO1>+C?k)}r)+^cXmXD$qW4$k_isKfvF;1vvjJ4ye6bx=T$s)q& z;HW8+svb=%t=SZZTi2GZ*<_?0XnXelEUKw_C)SrRQPyhPe^gYF%{I4a6xOJ6w^UfU zROL!qrXT|rt*4h9Tr97>diS${cV)G#%cg4V5IhydNJ3q@V!~Ogik=YIb5SCa72l`G zw4`z10hMk`Lx0Z@EfJ!B_A%$zL$Ubb2+L!HRhK>Jy0RFG?u+&{A*XPi5Q4eq8b7

_DpQl!1Q2&HY!NI(aYS0}iDEjiB7yr!scH&bJ0V1482cjVYuNp)?9I#pxJ9-9 zCMd*%%?%5tU;nEnN8E|)5~4K}DNLF{$Sk({ZwOTE#H?lcqo|PM|KOa|zEI^Ki5s{+ z$1{E3D$Q4~eR$kXM8QI-5eH~_l!I=OoRMK!73i9z^qMUo$<skIh^i zpFZcstn~lApR+b0>PAy55?f@(BFvlox+byRtY3KUf;-ZC&ob}xR*@Ic7frvvzUlz$v+oQV^pT>dQySWe~QxExIp*ZK&T<3zK% za4T5}{wX!_XoJ=mb2nYv5LEy=e+tEOBl;_wu)w@kc8rP0N9I|NMY=ofu3 z;8CN`mP+{VoDC(Mr>z@x!~TQpo=BmP(Wc+LwW8p_{s`RCY`TP4M?ZD?%!tk&0ToZA zF>Hg?MPH{U@YI6CS=_Z^k4Vy@zI?SWb_Lj2s?RIcyh79BTertqqU5wVR$8SaGGN2T zBECmJsAaTjDzPJufNlteI<{>$qlapo$8-_jN?=1E2X^=JCz>u*u%rzw(q3)FeNoY7 zwPkCCHFO7d3wpg8$GwCadm_y;F7yrXTN5^m9RZ($Xkn7W(7v)=9HWNEL0q6_89W&E zV#I`x7S;gN)oYTb->fhEd($j~krxB}dz#abfLsfekcRw9&b0+u!MXvqmCyv@@Hg!3 zMSbBvG)*ee%48e4f2~z}`)<~ZXYF+Jgz*_nIpmpX86S*D`LD*>(2^jkAuOWioQ&5P zsif-!nFp{<0$ChS68J2JZPTuQ|0zf+uKoV$3$TR-Cc1(@T@e)Sx8yISIXMj^H_!ZG^<1)`kl{T(#qhn3e~Q`oe!S3{k*&-0tT~ z>5m#M5zmDsG~h*}(R6ct;XhfpS4xSEsZHo`tM?yU;PA>Q@CPnZi%WL~%RZA`WcqDL z*x_b_1F07#5){E&;pWGwIBYPpSa8dayDqefbb}ZYDU?)or|jB-cduC{DM*_2pP88q zab=vdm2UXG649#g*BAbaLloMF7b5<4P)Xj-@pQkmsfi~RJhtMsQwFNWnBTj-U^sZw zj<4dd0;mby5QMyovbgM1U%t5z6&Da7zrt@F7@1+LNaDa}E_q}B;*XwlSHukb<&`LV z@WgF!@^i3<*~>GjBejz)q0zM>Q(yS697*8)0@zAxU)NE-8$2 zPM7VLD^=wu$#EeV~IBWE!YvVG{Afb$Y zWLj7nFM4X|zY zOOsO!*L+wKLQ$UR#~m@mJoZYHMpDLF&3y1;&LYv|lLI%Mvi|F`S|8%F|D?RNP1+ZH_%4PXm2O^>EYl4( zB}&h}IfRl-(6!6ryx1dXJ%}I!5DSRxOT8e{jp3e{rXL@~f}5pdT&AeF%Ky1Yg&A>y zSc4Rt=E9u(fdQ9f9RWNSsg?1)LAd?@xx-aiferWS3;*PqpfxqY(*`xByz1H)AEvoi zqZe=5gdTe2!!xT?<-Q6-G(Bf-!d$Dm{>B4q8y@=9{c#JKF&k!G_#h`|mxhHP@@ELg zi`d3RXg+o0rUkd#wL33o8~AiwR+E=Y3FhcFIOlU)G&hD9JV&;lKK9|g6}SB`IT4yt zGR(O70pGs8lYzB>bK-j1Q1hWXfY{8U8{gJQayU8JLwkKXz;8N)>*pxmC*ejl4L*8-Bt6Et|?RaL#buk*Vj1skxW+0xh zV9VM?4PmMvzHWOTv-+Ju><~wMj;@XX<7ZUe{NP7gMAn8)Qj7L+!dVT{JT~XBNlJ@j z{jBHNu=`Ki;1P;Z(=cJiB|BoD7UR#p&*TVOvCE6~Ke$aV9x<;_w`S(lpLD!$S~1~8 z%!g)H^p!Vwl3EDK z7-=8+-*;}chhgTm2M^nLnE~EWq@4V}+=`S3SADoG{Xea&ZWEow@X0UHHWWtB74@fT z;~fX>%I273ye4FPB3cq&l((@%-JFXWuH1h&^CbpO*?;t)7Qy8n6;O6ZP#KH@!FYSA?O#&mJ<>+Ps~ifr0iJuOuzFP|a3R42 zN-hL>kim#W3Zb`%^9o2gluj*Q-GoXfykYhp7!=MWFqgzoS_bwlCQB!F#E^}nTgPxh zqW@vD+SQlA=P=UJXzQ_)hT$|tMR*x63+7rMMa|RfzZh;WFT{GPMNVj0;i0U&ar8T%4>Jv zT}~S$#9>QA4%;OBoMB~$MW)AYyl9hViKh-V`PhlaE;aF3I`%?f>p_|w-i#AL1ZEjR z+ptmY3n^k#zxqVQU=EeX#V!UYH}JaBJmvJ?Wk0Z23n-SOT@H=F- z)-g(_bleS1qbF(si$r81*ggF{jQ8Dy9@!SDsNM^*wN-Vf7hmkG6qt1N@R3CdMByMV zhGd|Tb<>xUikQ@oxn9Okg9{^#qS0<4DMQSoe@!y86FhY6HzxO045Xn;iyP@K-UNj(B6>veK&Tsz~j-}-gYm1c06|AwQQwX z#?#s`*htM*)PFs6{Kh`<3SoRzcfFpOO~?f$mNX1GSn?d{;9kCEWk{8zNr{1j3ojxn z%~3_Uh(O%?>ME^_qjgI%7_+d7tv9jTVWkPj%;>PFo0ZCGQ$kXXgo0wT-L-Gq+E zgW~+nxY$cI8|u-zY2atXO|uf_Q76wxHLD5R+zbl89+`ykvJgitHVv;6-aE>@F#+zx z&$60+M~}rw{i2ApbbLxRm#>KVWfsAQ(+v&USO4{xecghCnnYo2Sx$T8U=|L~36|W_ zoW|e+PRPs5cp}cE1zSbzB+c@;A-Q6+@JvluYC($&MX~&-z{kTZ!!r>Y5*o;1z<7^l zS00}eE*I>*{CyMG%2%?uL^6AdT1ApKVnD_wvlP5YYCOlcu$Lew45JjnIrt})w0C2= zQ9YpZ|6#>do2*0nq>gYbh>7|EFX@9{i?@`}4t^wrE1+fxnVd$gh#?lHsmWKz=_&`P zI9di@tuV9HBjQ^wIWMPtn$YdW(gcoK(QMaDlKWaii;c*nV@aB|bw*{T^+i=Jy&G4K zrle|WE#ONLeI@PXC&9!fg%cVMB@r`Wa~^YcwXCNIf?5My0yyKm(1*d*DkNJomP>Pg zOnPX9t~(|(`7WH0lxd}Kh&OwvSv+K&+)_lYgxvyf+r@3Lo~RBSQs_6J*=(pTUDj1A zQJFNU3eAq{K6Pd+Ah$hnP{un(IgANTZ(_0J*K@hUf#P(t<)MnXgcQU%PsI3WFA7m^ zV`--#id=wM23D##EDiRI78x996flk!(IKZ`W8GboLVd3Tq^AA6dy3Y(uW?_+)Ch&h zzHMx1z+PjiV@E~HVo0V`wui7N ztJQ|)a%%WFdvYWt?vC;r0ZOb3xE{y6EZ>U@Ts|j!bTbV8Sa!)erMrsNt{Axx)x1I1 zEH<|FuxM`Jiq@Ev)|=q~y7C8d^ckXq?@Q_}2ge>%Rp}znOIJ7=V&U z6@;Fq1BobD2u>xi+@##OE`u3+GT;F8Qya^~P8OskMIKJ#eh^6^xO_M#VQyAfkq{>w zmC@SC0S9cVu)kKVTxU``dkA;gXMagrR%Ut@osrhTe|Z}nIm9yN5Gx9q>smi(>%qR90mE_hf&F*XMD6k%I5;T z00RoIKN_h2dZSA#L5}fmG1|VOtZ0sRE1Vpm{vQ`6fp%H{#S20-{+o`WqVY;fHY=wH z!}*_C(r}dThXl+S>aRrtX_LUP0=A0FxFy~q3SSO`1m?88r9}h8u=?o{dQ-sh6T875E-S{Z;N-^a@idbHoq)7_+I1m!wh3C#vefwg(KAacrpndT0BMlY87Q zC0^#d4aGZ?kdAsgCA(VWS-}!=bb=DgEPj#&jU&U+M3$0Np(l=7jm<>mR+-K|o|J1a zN!D^WIaVX^@=4@vXEzs3VvK&k5rsoxvxcTPya{D<8kBQWk`qRtdthg1YY87Zwf;A= z6*Vc7fm{^p`V`d462^_R&*rjpgv#xctjoL>Q3ciNHL|A`3EDH7c-|x|63l|Q%U9A4 zOZ8u~>@^XB*lS~e$t0r&viMeG0r8|;ywlGFzI(B>jq_%>KcY==2}h(7%85P`SfP0@ z9P zDdvFkvERplJ2W8z+24)g*+!hQ>n73HH^S7p*kADL<6>Y<$PkZcP;>p)Co9=Htr^!_ zaMOd&R~FZIQwTse4hUR};E}L+z@#`r6ZV;?d;#RdV9I=<^2q@I{1~4+9RLxLBrH38 z*xOKadeYCVImw-1+Zjvv9PxVu7VD)aFUjjd_+LrnWWK6}m)I$zu>%YROwiUO(N4(Y zkQur1;!Q7H%;rI=H^<^jgvd(Jo?n96?fS1T<^*t86H!(Y>g%^8-lrt8$+djXjJ#h-Q+gs@KP9-cG%;V&&e7T79RAYqUJl-8A>zeO&bu zDaLgI`!uy{w?W&obsHHTvs{tEl9v=fQf!L2z&Pv2l+>SDE0f%!IHqJ~fW4AV+?1Bg z9Pt~ybWvkTOPwB1;11i#dE)>=a=Tg#qda?hhkH&~We~!Ic`0la18i%dyY}H?0C$3T zDOH|}^;Qb>?DXM95EJuW>Y*&R{n9Qzmp`niUtEi!1}d_Ev>9O}WcD3)j4$O|5z; zuV%zG!DW`Ah%T`d;D(h$SlTELk-ZaUAzu7apcG6BJsu>nJ z+5nqFfBh|e4Fe(UR%QkiO$2)V*WdBRDB)wvlG`2>bOPz|6ZkbCb>v|E*S`{rqDuXo zBiqTc-%T$PPS%$gxtg&$fqO1w_4jPul9rKU;x80_2GJqNFNpzZ77MT?feL9vG_96!pJp7A zcg2PGHZnD1$5TrY(isAipVsGQAPwLQy!4l81PsGSrxn{_ajueP;KNq5htTFkRx;|<4F&ngW%A}T@|8~%>j~@wRn-l7Nr`v?@#-~SOL9?wWKFz@ zs6ByDk}k!tblv(58#C$)z5ip-EPBE zIUy6ySTJP80jW&{m>yF@{qTsVZ-L&fPM>qBX1CneXlT^_?$m$%xoG^Y=o}=6?xO@* zZin1`91&_w9@EiKu~o9G0LK)_O#7utqljYt*I%hN(!%Yl|N1xN@;!~!G9e14UtYIX~;&q-a<@|pKkV$N<=~20oiT4z|NHi*9R($gd!NdGX zx*n=QEDg(@Dooncie4H%qf|C3&2y)$h)AGp!bK6tr#Ug8W_*^0tsJ&X!xnQ=QOX-( ztPp(Uz-`gpC_@7~xD>u?(i3{dhQpHIP}2mFoD^u(Dq~vDW!v}azy7hA^-YTU?HB{D zvkjlx@y?15HhdG4@#5Va{o7nCtRD4d)+-l;%sBAL)d)u(T$FUw*U;&9?2KT&l+!L! zs+yUVIQ8kXA8)u@)^V4Iu=)a@>BDPjGPvmz*6^B~a7=ys9>@C#mNbCg`x*7me09IH zbI{!m@-#L}A<&9*+#4@HKmW*$|F5PiZHw~CwpphNs-Pxn9x0$e=FvpZv~3iJm^dX0 z#waFgB5GPx6cuqmKtWMNP!W`=tvfwQo_l*9Z|8ov_e1yJs9$o|df%t>!AMC}y=U5c z?X}n5hZO?M2YOfsYlS7`tC^m~l{6mPyI{8P?pGop`7X+@x@gO@HVL_l=fb$r+=>Z_ zuQQ#*C2k*DPjc#5yjYW`*nrOkmfaXs$U*9jd>JtWDjnt|%Net^b@gtvG-Jw#O`VQ9 zJZr{XpQ=xS=q$nMa><`GL*o#n;UQ$tEH^7J*;GVg2CD?)0;69BaKCXVR7+x9z}jyMPiiPAdG}k(AcnIWCwJjepjSm zs}a}a^2_CLmHau&Ip$Bh6~Z^-T;bjCX|wf?p;dy08-6Tnr-acJ#|H`Z>gXaTvN?lR zi4wzBPM@VvgF`X(36axc3kb=+VsfXIIOCD>MiJ{JnofS=XA!xfKk&#ZD7T8A8f88I zXyK!{T;z?_;t1YG&Gq4!5_SwkANkDuqHIZ>`uk_mX>&%k15BJu#@X5i^V;hPbR-3# zdWmR0lQh3w()um9dh8#Zf-ipJ*B?>Ch(;V&(PtB~ zf9I~AP>~3IX9LzN5SCsb*UOJP+DZDOvJT(6(bPcU2X|I8Mt!EaMKYB}KipD-Z$D>c zl)A&{wsBduzW4>*FFA&tN=Zjll)baKs7U=qZSagS zO|?CM6CE^!hstp%ivy~P`Al&{jvTXTYHc)|xAb!)*>gNb2UN1#t$O{V_c--OM>%^3 z8W~eHV{b3bUauN~9O&bZ{aWE>9B<=?bI(*Ou@dv6MCB73(f6Y0|M=Ejf9D7Hse8sr z6&UZ*BJ~|%WLA%T&p3vCJ!Xsxvz;k&bbE17d%w*RhtsF8=I~&Vi_peJAQS&(o!YXp zB;MJ7?)(L9+Dm=-i626s`y&^Ljnh@b@kQa?pFJI9M1z7lX_Toltb3HdaA=IF_%IH% zpl_A0t(>OBpcQ-N0vV)8E>fb9r5xQV0nAFOK;MxoM-vYZC-rnQ6F#^h&{ILH#qlH> z8AjH!eE`Qm2OzfL7c=+=KULjHObE9lm@d5gi%vM*RDVv5?O&x4RE%A3YoDqxZrBOb zp)hGohv=(vG^?_+TSq;N;uMwyx7sP9wZ8c?{k_pXW^OwSI5-MjfwI5w?ytTvhh||~ znu=o@=2EdkPWMx>06VsFi4W@2>TyB>rmP6hBQ(js-~r}%zp27%8X>#FCE7vzT*Yc) z{;__9E7JtaICY?#2Z{D**rUc1X$1N>=4SNj@I>&q@b13}TvV~$E92q?Yh+?fGIEM@ zxTLgu8?xfJYt&1q8GAJO6ps`W9W<&DK`plH8SD6r9C4cVoL{}I@a}K)q5}-ouEizH zVIZ48R^Q;ktyNbLZ^O7A!D8IF?v(IMu~3l8G!YlgRR3|~q$nOL4&q7#S7NxFl<@6t z8WXak8MG^Z6u?z#f%wF8GN)Bo6F7*mY=c4%cL`o5F`HsTFB+e6bOm7ONa^P(xS}tW zxm;ct|;=bPr9GBos*b?5%6HGv{j=5OzWw4OtF!_4XwuSI^-t{LAU}{6 zs0we=CR>rkH}i~0GdM0|x?2Duf^N0Mt6;ee8POOvtLe&giCjn(zU-xZ$IlB6Xe4Hx zctb4^zvT}gbm+ZEvTRtoHQWGML|noD9e zh0-+3$@2HfcdOBZYlUNDVgDErQSbNsqeq-hpQ4k- zAwR|h{5gK-VRf*&QcpkB4pybK;ag3eo6WYkMauE0kRNw21~cs#Y*77@a#!Rg&(qI7 zc9EvbPjG?6eI7039}qO^Gmj{J*RCF2eyU2j2}+dnYEaRG4(wOw8RP}j98Ktj@#bl6 zgHOE_RdQ<_Vx24hk)j_>MQ?vkUbzcUL2}zoH{^l2|2%uB$ zrPX53V3i0>szJp@9824c*%Ne$*+hY!G>)jb2^IKjPvD>VBsF_eL}M zm{wVI7{7CxyoJWGNSnyWJkusAtD4)N)x0N(E%Q(01-Y{O3i+oD`L&>=?WNSUiW^d@ zrBtMDsq_7a7VNM62>6)Ta)d?{K-P(+;ZBlor%JQxJc8pm+u2ROHp2gwG=ADeW?+I(PCq_y`q3J>J(jzX~mHlOvz+tnbzgM-pIpVQ)~ z#R-dN)sC-8+v{^Nzah3y<-%mBV9d{1A!`tuHIg7Rvqn+MUN1I^->y^pt0X(-b6T7x zuvuSFPdoE(;Xv6` zK?ZBE2$yWu|EQcp7%#=oNB)@u7^f#-dz||d{MJn#_7#)YAD|?qrU=tfH5yqh_zsQwgM_IZE5)oI3j#Tfs8G!Cw2SW-(du-W z(Ps%=e?qOr5K|YlRtPCH6_!kLWO=G^0U*wvqBTcIk#>cf09XL?PFNqu`4>4Jz76$4~zzm$Q zD(&-I?ox4Eb2{XeM1lEb#mijSeg}pGK%98XHRKCp4y@`76;Tny4I0HZ9i?)q=!CJ- zTCNR}_=z07>sHx@OiV&pKa4#!MAvx|{VazHfk7baR1$ckawv9AS_>QR>j+*&&CE&+ zPve*d?O&^8!On$bxOc0Gvo z5MG24h^hO;gAy~rZThfOsS>t8mL=qkIZcwsk@$A@_s_DcVPhC`J?NB|PH8kRMs?zcMV(&=UoD84^DuN0Btg>`-p@B)1zS zUsOU~@F43|7Pd&5>9HbQ_wial(NZK$PH?Bz<73+z*GqlAZ3THcPj0-mq)kzg19M|m zS-WQJ7;`Rh;A>tif6uR7&@9X)ac`A+=$m1z#o$Wf0VO?Ng&FJ+f{!yetnv4%6T!x> zwUMfDF^iLedl59JusMrU>WWxvsf-*|_@oA(wo!RYk#`Ypmf#m&e9c4TbefUHB6Evj zC12_1qjURHTen*tFhmZl_vh6FwupxwMAwJV!>s5at6A;0eIp`)+R?Om#7z9*KNN}Lkx zWUnJWU_uY*m-GIm<-Duah*>m*b@f38%W@iit$dTBazT-g3;A~>W3=Psq@>; z=dqx9Z2;jyK2N7AI^Lo}BFxZmP(F2dRJwL1!(h|008Mt1@|$7?^9NhhwpSKATgd-Q zP|eE%NDB%0aagV_K*PhSu4EfjGPaGn&83t{$a8$C5RRz-e;K=a@!xkhI0V%`$*B){ zK#W4Ax|p5j|Mt#sa6w%YAb_Fnn_QMJ0=iaVl&`aH<0|)4g<8zTvFij~MhkZJF?I>a zwEe4P9~;CB)u_{$DjA6&{*AhbUVa=QY$JwGo4<3PPyE1t+o@>AOPZdeuTfknnQ6r4 zqsSi!IrbFt|86Pa+7K5-(1D#T3gClyZEuUSJp^>EIN8hMn*?_J!1-Y>w6N~iI>P}m z2b587jg(Hk$ZxBAX*u>N>^ZEALJ%i}^+KpB<TE zfVrUj52cFuO7UWVsZGa%tih`YhNF(>uinNn>c@J(EU()(>WvL7xXh<%%|ETPDkU8T zubsGF%4^;@`mE-yj+H_@y=kfmh%7Eea50IQlw%`ku~d(f!XNBh?ymu3d%ve|qJrf-1OKlu@9|x-Pi!CWa9`UWGbY3*TC7J}_@| zd4}H)kLm}PSOca;3MUzDo6XxcV4MAft2#iB{nW7P1f_9Gro0UEA)7Wx^DaS-BuqQR zrGDtbxErs8c%v}d1s?Xs&Honx&=O5XNfgcj>h`Y2C5U{MU~`lt?v~2xUq8jvVmo$k z#O@9FSa5wKq8d*OFXJjVNih{u95Rc`Q8PlElTNAtjP>qTx;lluB9T6klZ{Z(h(({s z4OOy~2Ti;xtWbTXR2qG{I60t8IV^b)<&!cF9(3VhQgviG8Sb1?`rFc~0M=TF7j}g4 zvk10i=#xn#l#}`>3gcKcc1O)VG9aw#{i26rkqV{{sQH31kDoboZA8D%Z1|?vY-Kv& zJ0JRW;&*Y2`71F9^I)>daem}sj!H@gMx40m!D&BggKQxYGELhfXpNyggZHy)%_vjf zp!uiI)a2IDBXAiA_eu)+Uol%CNWIOL9Np+4pbZIIsMa|nL*!>_H~(w5PE`P}ZQ;#P zEqD_ydU^Cd5HAzgO-0H`?bjJtofc)OCow92s-#+-R>kh!JeFy2JW5clJJ-0)It-1AWg%TWwPSnx;0 zdozTUh{LnI7Ud;{#yyrm#&3q-2F&7GzvT%i;1pq|ydkxkSAqzH;0fyhF~URCyFCl-AdMX5KFEK^Lj142-R^@GdZj{m3X)uW3HOrWU0{evzszc3lVrz zQZ>O+LSB%!ilNSL;#YJq67UxCzbm03chr(*#kEFU(yQGz*Kx4Nq4;z_=9Ff>1-E*+ z^Qv)7`lu!L_vP!|EZSwVVW<^38PH~R%#icnFC3c|)cd_<5@NbrVp`}mDSL3e1ov$s z;^q=RWv5_384`j4akSeAn%}@E2+xg*@6LMEpr(fu%dQ4pH>zeYqGWmCC)`R@CUoGF zL`?i7FK%miUd$Boe{kDX_eYDJ+K?xe8>^h>2D{PA1AevB;t|HBPHMwVg#f!nG5aZY zRmgZ=J3WZfJ}Y=7fAAxJVo3VOw8E*l`bqGs%8;|o#7f-M+*Zn#wWDrKD!}q{76>|W zOzaVGr%BNH(9^?tIG`;Mk;-W+f0t@l>A?5+IIM zhps6TTXI;;Ta4(?{7@8RW9~3=5ef7kdGYi|?t0z?rH&kI^A#1@8bXbBWEjDSN|8o2 zQFlaHh7eHobU1|4i0<#P>S@c6l<|q9g9UXQQkl+rD#UK&WZI128E%BpoJ3d3Z0ho$ zB7o8gihXJC=)NW$X7qKDQneQY#^uo|joKV~v6RuW6gNi6*!y(lDanvO71>3>s|xj- zFww~Mi`n!|01o~p!4}zsKU?ayCiK~1{=)wfv$UeG(+as(xIwt44>Z;ouZHFNwdtk= zMZ)KW=IRs)|0Sw#D#3F*?wwpRZbk$c(R<#o%+(otz4#_*!f(P@j_`uGsLDN^9_EM5 zFohF6JOE4;ZeaIe&YT}ZqN-gkOeI9LkfSA&Vzzup0U1p($(%Z}YK|We?8vA z&DtVumcg7C-*9_SN3!C>DXtP@vRT7I-5i57GkDd-dl5aV664Lgz_c;rFP&y}O?Evl zyPiO!!j%p(FMKJ+t1DsFa z(TNiG?bYnYuz}1vxf64f2Zfo0IF?GWkivZx)?N?cRoD!@jY_e>J3vWaOQ6z$gz!{@ zu4DMv&LR+@#I-N)C6vc!E>hi{PimRtH0Ac5eiqfXzq?9ca?2+fnBuj0O=%ehgSZ;f zttFQe=J{F@k5iQMTM44KNo+|YQh~FY<8t!>gQa#(vLlEWQQV1PjMmuH;aCOzt%;JI zt+gZJG&W`Mp)gjpE+aWCla+>_^Upl>Q-^u@!il>cT=1FGXUioIJ*{I?i5peUp&QSa zBiI~cA+y=ol>t1#w`E0 zBpESB%$VA^d9|D{3+pmo#h9XDYM9)mqP>MgY)i39Qv=Q!sC17|d)Q5;V&7ee5(FQNMkcDLY!kYPKnDK0fa zN*BDS?j?y-iG>Qh*~&!nf)8gZ@YJqIKBC=PgTu0gE%;JkB+5bzQ#x*s%8F4tL zS=sl{>rzQGkJVaA>RFbklIB*xI>If)nVQoFO#3i$(IHH+B5Lr)00G~ID`8!k%sqZ5 zsrz8!GK?YWQ~0(rq*uAeJtdg(;I435RG?`~5+8EKc}x3uFpr;9Ij&t_31MrIEX07D zU2vT7WcfGjS(L#hKYSJF+)AiG(vUqxE-AQ8>XMnO$~^E+jW`+$`Ja;atzHBUMOocK zqxZ&)NECwH7>yhm&zvs*mEk$8etvzmQjt{PgLwwVS>1^sf$n9|W*(&FNhuNj$@f8lu)`e%#URd|~Y4loI1o6TyV!u)-=Y$#*V88!i*^ z@@!{=CXtL&&}JqddvQ-m9XG}UCRM3mgH4wPQ{bIwRzHG>?w{-8Zfw(hh7ejMu=Pj9 zO|yB(V~@Ev%W37g{&}1#CxW1yx%P@~h8=?pp1UyUR%cAM=CLjiNl^ApGv)K+>L^p) MVeJ10Kl8x<1F-ng+W-In diff --git a/Wallpapers/TemplarAssassin.pic b/Wallpapers/TemplarAssassin.pic deleted file mode 100644 index 7daac9774bab7e627427eb44de2318102d7152ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12485 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 IDS_Vq598X9(f|Me diff --git a/Wallpapers/TyanSunset.pic b/Wallpapers/TyanSunset.pic deleted file mode 100644 index 90ed02efdb38feb905184aa6ea691a43249d7dc1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12407 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%0D?pe_OEdPtLBa?Hyh{ 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 diff --git a/etc/profile b/etc/profile deleted file mode 100644 index d77e0c89..00000000 --- a/etc/profile +++ /dev/null @@ -1,29 +0,0 @@ -alias dir=ls -alias list=ls -alias move=mv -alias rename=mv -alias copy=cp -alias del=rm -alias md=mkdir -alias cls=clear -alias less=more -alias rs=redstone -alias view=edit\ -r -alias help=man -alias cp=cp\ -i - -set EDITOR=/bin/edit -set HISTSIZE=10 -set HOME=/ -set IFS=\ -set MANPATH=/usr/man:. -set PAGER=/bin/more -set PS1='$PWD# ' -set PWD=/ -set SHELL=/bin/sh -set LS_COLORS="{FILE=0xFFFFFF,DIR=0x66CCFF,LINK=0xFFAA00,['*.lua']=0x00FF00}" - -cd $HOME -clear -/etc/motd -source $HOME/.shrc -q diff --git a/lib/ECSAPI.lua b/lib/ECSAPI.lua deleted file mode 100755 index 42d00b0a..00000000 --- a/lib/ECSAPI.lua +++ /dev/null @@ -1,2241 +0,0 @@ - - -local advancedLua = require("advancedLua") -local component = require("component") -local term = require("term") -local unicode = require("unicode") -local event = require("event") -local fs = require("filesystem") -local shell = require("shell") -local keyboard = require("keyboard") -local computer = require("computer") -local serialization = require("serialization") - - -local gpu = component.gpu -local ecs = {} - ----------------------------------------------------------------------------------------------------- - -ecs.windowColors = { - background = 0xeeeeee, - usualText = 0x444444, - subText = 0x888888, - tab = 0xaaaaaa, - title = 0xffffff, - shadow = 0x444444, -} - -ecs.colors = { - white = 0xffffff, - orange = 0xF2B233, - magenta = 0xE57FD8, - lightBlue = 0x99B2F2, - yellow = 0xDEDE6C, - lime = 0x7FCC19, - pink = 0xF2B2CC, - gray = 0x4C4C4C, - lightGray = 0x999999, - cyan = 0x4C99B2, - purple = 0xB266E5, - blue = 0x3366CC, - brown = 0x7F664C, - green = 0x57A64E, - red = 0xCC4C4C, - black = 0x000000, - ["0"] = 0xffffff, - ["1"] = 0xF2B233, - ["2"] = 0xE57FD8, - ["3"] = 0x99B2F2, - ["4"] = 0xDEDE6C, - ["5"] = 0x7FCC19, - ["6"] = 0xF2B2CC, - ["7"] = 0x4C4C4C, - ["8"] = 0x999999, - ["9"] = 0x4C99B2, - ["a"] = 0xB266E5, - ["b"] = 0x3366CC, - ["c"] = 0x7F664C, - ["d"] = 0x57A64E, - ["e"] = 0xCC4C4C, - ["f"] = 0x000000 -} - ----------------------------------------------------------------------------------------------------- - ---Адекватный запрос к веб-серверу вместо стандартного Internet API, бросающего stderr, когда ему вздумается -function ecs.internetRequest(url) - local success, response = pcall(component.internet.request, url) - if success then - local responseData = "" - while true do - local data, responseChunk = response.read() - if data then - responseData = responseData .. data - else - if responseChunk then - return false, responseChunk - else - return true, responseData - end - end - end - else - return false, reason - end -end - ---Загрузка файла с инета -function ecs.getFileFromUrl(url, path) - local success, response = ecs.internetRequest(url) - if success then - fs.makeDirectory(fs.path(path) or "") - local file = io.open(path, "w") - file:write(response) - file:close() - else - ecs.error("Could not connect to to URL address \"" .. url .. "\"") - return - end -end - ---Отключение принудительного завершения программ -function ecs.disableInterrupting() - _G.eventInterruptBackup = package.loaded.event.shouldInterrupt - _G.eventSoftInterruptBackup = package.loaded.event.shouldSoftInterrupt - - package.loaded.event.shouldInterrupt = function () return false end - package.loaded.event.shouldSoftInterrupt = function () return false end -end - ---Включение принудительного завершения программ -function ecs.enableInterrupting() - if _G.eventInterruptBackup then - package.loaded.event.shouldInterrupt = _G.eventInterruptBackup - package.loaded.event.shouldSoftInterrupt = _G.eventSoftInterruptBackup - else - error("Cant't enable interrupting beacause of it's already enabled.") - end -end - -function ecs.getScaledResolution(scale, debug) - --Базовая коррекция масштаба, чтобы всякие умники не писали своими погаными ручонками, чего не следует - if scale > 1 then - scale = 1 - elseif scale < 0.1 then - scale = 0.1 - end - - --Просчет монитора в псевдопикселях - забей, даже объяснять не буду, работает как часы - local function calculateAspect(screens) - local abc = 12 - - if screens == 2 then - abc = 28 - elseif screens > 2 then - abc = 28 + (screens - 2) * 16 - end - - return abc - end - - --Рассчитываем пропорцию монитора в псевдопикселях - local xScreens, yScreens = component.proxy(component.gpu.getScreen()).getAspectRatio() - local xPixels, yPixels = calculateAspect(xScreens), calculateAspect(yScreens) - local proportion = xPixels / yPixels - - --Получаем максимально возможное разрешение данной видеокарты - local xMax, yMax = component.gpu.maxResolution() - - --Получаем теоретическое максимальное разрешение монитора с учетом его пропорции, но без учета лимита видеокарты - local newWidth, newHeight - if proportion >= 1 then - newWidth = xMax - newHeight = math.floor(newWidth / proportion / 2) - else - newHeight = yMax - newWidth = math.floor(newHeight * proportion * 2) - end - - --Получаем оптимальное разрешение для данного монитора с поддержкой видеокарты - local optimalNewWidth, optimalNewHeight = newWidth, newHeight - - if optimalNewWidth > xMax then - local difference = newWidth / xMax - optimalNewWidth = xMax - optimalNewHeight = math.ceil(newHeight / difference) - end - - if optimalNewHeight > yMax then - local difference = newHeight / yMax - optimalNewHeight = yMax - optimalNewWidth = math.ceil(newWidth / difference) - end - - --Корректируем идеальное разрешение по заданному масштабу - local finalNewWidth, finalNewHeight = math.floor(optimalNewWidth * scale), math.floor(optimalNewHeight * scale) - - --Выводим инфу, если нужно - if debug then - print(" ") - print("Максимальное разрешение: "..xMax.."x"..yMax) - print("Пропорция монитора: "..xPixels.."x"..yPixels) - print("Коэффициент пропорции: "..proportion) - print(" ") - print("Теоретическое разрешение: "..newWidth.."x"..newHeight) - print("Оптимизированное разрешение: "..optimalNewWidth.."x"..optimalNewHeight) - print(" ") - print("Новое разрешение: "..finalNewWidth.."x"..finalNewHeight) - print(" ") - end - - return finalNewWidth, finalNewHeight -end - ---Установка масштаба монитора -function ecs.setScale(scale, debug) - --Устанавливаем выбранное разрешение - component.gpu.setResolution(ecs.getScaledResolution(scale, debug)) -end - -function ecs.rebindGPU(address) - component.gpu.bind(address) -end - ---Получаем всю инфу об оперативку в килобайтах -function ecs.getInfoAboutRAM() - local free = math.floor(computer.freeMemory() / 1024) - local total = math.floor(computer.totalMemory() / 1024) - local used = total - free - - return free, total, used -end - ---Получить информацию о жестких дисках -function ecs.getHDDs() - local candidates = {} - for address in component.list("filesystem") do - local proxy = component.proxy(address) - if proxy.address ~= computer.tmpAddress() and proxy.getLabel() ~= "internet" then - local isFloppy, spaceTotal = false, math.floor(proxy.spaceTotal() / 1024) - if spaceTotal < 600 then isFloppy = true end - table.insert(candidates, { - ["spaceTotal"] = spaceTotal, - ["spaceUsed"] = math.floor(proxy.spaceUsed() / 1024), - ["label"] = proxy.getLabel(), - ["address"] = proxy.address, - ["isReadOnly"] = proxy.isReadOnly(), - ["isFloppy"] = isFloppy, - }) - end - end - return candidates -end - ---Форматировать диск -function ecs.formatHDD(address) - local proxy = component.proxy(address) - local list = proxy.list("") - ecs.info("auto", "auto", "", "Formatting disk...") - for _, file in pairs(list) do - if type(file) == "string" then - if not proxy.isReadOnly(file) then proxy.remove(file) end - end - end - list = nil -end - ---Установить имя жесткого диска -function ecs.setHDDLabel(address, label) - local proxy = component.proxy(address) - proxy.setLabel(label or "Untitled") -end - ---Найти монтированный путь конкретного адреса диска -function ecs.findMount(address) - for fs1, path in fs.mounts() do - if fs1.address == component.get(address) then - return path - end - end -end - -function ecs.getArraySize(array) - local size = 0 - for key in pairs(array) do - size = size + 1 - end - return size -end - ---Скопировать файлы с одного диска на другой с заменой -function ecs.duplicateFileSystem(fromAddress, toAddress) - local source, destination = ecs.findMount(fromAddress), ecs.findMount(toAddress) - ecs.info("auto", "auto", "", "Copying file system...") - shell.execute("bin/cp -rx "..source.."* "..destination) -end - ---Загрузка файла с пастебина -function ecs.getFromPastebin(paste, path) - local url = "http://pastebin.com/raw.php?i=" .. paste - ecs.getFileFromUrl(url, path) -end - ---Загрузка файла с гитхаба -function ecs.getFromGitHub(url, path) - url = "https://raw.githubusercontent.com/" .. url - ecs.getFileFromUrl(url, path) -end - ---Загрузить ОС-приложение -function ecs.getOSApplication(application) - --Если это приложение - if application.type == "Application" then - --Удаляем приложение, если оно уже существовало и создаем все нужные папочки - application.name = "/" .. application.name - fs.remove(application.name .. ".app") - fs.makeDirectory(application.name .. ".app/Resources") - - --Загружаем основной исполняемый файл и иконку - ecs.getFromGitHub(application.url, application.name .. ".app/Main.lua") - ecs.getFromGitHub(application.icon, application.name .. ".app/Resources/Icon.pic") - - --Если есть ресурсы, то загружаем ресурсы - if application.resources then - for i = 1, #application.resources do - ecs.getFromGitHub(application.resources[i].url, application.name .. ".app/Resources/" .. application.resources[i].name) - end - end - - --Если есть файл "о программе", то грузим и его - if application.about then - ecs.getFromGitHub(application.about .. _G.OSSettings.language .. ".txt", application.name .. ".app/Resources/About/" .. _G.OSSettings.language .. ".txt") - end - - --Если имеется режим создания ярлыка, то создаем его - if application.createShortcut then - local desktopPath = "MineOS/Desktop/" - - if application.createShortcut == "desktop" then - ecs.createShortCut(desktopPath .. fs.name(application.name) .. ".lnk", application.name .. ".app") - end - end - - --Если тип = другой, чужой, а мб и свой пастебин - elseif application.type == "Pastebin" then - ecs.getFromPastebin(application.url, application.name) - - --Если просто какой-то скрипт - elseif application.type == "Script" or application.type == "Library" or application.type == "Icon" or application.type == "Wallpaper" then - ecs.getFromGitHub(application.url, application.name) - - --А если ваще какая-то абстрактная хуйня, либо ссылка на веб, то загружаем по УРЛ-ке - else - ecs.getFileFromUrl(application.url, application.name) - end -end - ---Получить список приложений, которые требуется обновить -function ecs.getAppsToUpdate(debug) - --Задаем стартовые пути - local pathToApplicationsFile = "MineOS/System/OS/Applications.txt" - local pathToSecondApplicationsFile = "MineOS/System/OS/Applications2.txt" - --Путь к файл-листу на пастебине - local paste = "3j2x4dDn" - --Выводим инфу - local oldPixels - if debug then oldPixels = ecs.info("auto", "auto", " ", "Checking for updates...") end - --Получаем свеженький файл - ecs.getFromPastebin(paste, pathToSecondApplicationsFile) - --Читаем оба файла - local file = io.open(pathToApplicationsFile, "r") - local applications = serialization.unserialize(file:read("*a")) - file:close() - --И второй - file = io.open(pathToSecondApplicationsFile, "r") - local applications2 = serialization.unserialize(file:read("*a")) - file:close() - - local countOfUpdates = 0 - - --Просматриваем свеженький файлик и анализируем, че в нем нового, все старое удаляем - local i = 1 - while true do - --Разрыв цикла - if i > #applications2 then break end - --Новая версия файла - local newVersion, oldVersion = applications2[i].version, 0 - --Получаем старую версию этого файла - for j = 1, #applications do - if applications2[i].name == applications[j].name then - oldVersion = applications[j].version or 0 - break - end - end - --Если новая версия новее, чем старая, то добавить в массив то, что нужно обновить - if newVersion > oldVersion then - applications2[i].needToUpdate = true - countOfUpdates = countOfUpdates + 1 - end - - i = i + 1 - end - --Если чет рисовалось, то стереть на хер - if oldPixels then ecs.drawOldPixels(oldPixels) end - --Возвращаем массив с тем, че нужно обновить и просто старый аппликашнс на всякий случай - return applications2, countOfUpdates -end - ---Сделать строку пригодной для отображения в ОпенКомпах ---Заменяет табсы на пробелы и виндовый возврат каретки на человеческий UNIX-овский -function ecs.stringOptimize(sto4ka, indentatonWidth) - sto4ka = string.gsub(sto4ka, "\r\n", "\n") - sto4ka = string.gsub(sto4ka, " ", string.rep(" ", indentatonWidth or 2)) - return stro4ka -end - ---ИЗ ДЕСЯТИЧНОЙ В ШЕСТНАДЦАТИРИЧНУЮ -function ecs.decToBase(IN,BASE) - local hexCode = "0123456789ABCDEFGHIJKLMNOPQRSTUVW" - OUT = "" - local ostatok = 0 - while IN>0 do - ostatok = math.fmod(IN,BASE) + 1 - IN = math.floor(IN/BASE) - OUT = string.sub(hexCode,ostatok,ostatok)..OUT - end - if #OUT == 1 then OUT = "0"..OUT end - if OUT == "" then OUT = "00" end - return OUT -end - ---Правильное конвертирование HEX-переменной в строковую -function ecs.HEXtoString(color, bitCount, withNull) - local stro4ka = string.format("%X",color) - local sStro4ka = unicode.len(stro4ka) - if sStro4ka < bitCount then - stro4ka = string.rep("0", bitCount - sStro4ka) .. stro4ka - end - sStro4ka = nil - if withNull then return "0x"..stro4ka else return stro4ka end -end - ---КЛИКНУЛИ ЛИ В ЗОНУ -function ecs.clickedAtArea(x,y,sx,sy,ex,ey) - if (x >= sx) and (x <= ex) and (y >= sy) and (y <= ey) then return true end - return false -end - ---Заливка всего экрана указанным цветом -function ecs.clearScreen(color) - if color then component.gpu.setBackground(color) end - term.clear() -end - ---Установка пикселя нужного цвета -function ecs.setPixel(x,y,color) - component.gpu.setBackground(color) - component.gpu.set(x,y," ") -end - ---Простая установка цветов в одну строку, ибо я ленивый -function ecs.setColor(background, foreground) - component.gpu.setBackground(background) - component.gpu.setForeground(foreground) -end - ---Цветной текст -function ecs.colorText(x,y,textColor,text) - component.gpu.setForeground(textColor) - component.gpu.set(x,y,text) -end - ---Цветной текст с жопкой! -function ecs.colorTextWithBack(x,y,textColor,backColor,text) - component.gpu.setForeground(textColor) - component.gpu.setBackground(backColor) - component.gpu.set(x,y,text) -end - ---Инверсия цвета -function ecs.invertColor(color) - return 0xffffff - color -end - ---Адаптивный текст, подстраивающийся под фон -function ecs.adaptiveText(x,y,text,textColor) - component.gpu.setForeground(textColor) - x = x - 1 - for i=1,unicode.len(text) do - local info = {component.gpu.get(x+i,y)} - component.gpu.setBackground(info[3]) - component.gpu.set(x+i,y,unicode.sub(text,i,i)) - end -end - ---Костыльная замена обычному string.find() ---Работает медленнее, но хотя бы поддерживает юникод -function unicode.find(str, pattern, init, plain) - if init then - if init < 0 then - init = -#unicode.sub(str,init) - elseif init > 0 then - init = #unicode.sub(str,1,init-1)+1 - end - end - - a, b = string.find(str, pattern, init, plain) - - if a then - local ap,bp = str:sub(1,a-1), str:sub(a,b) - a = unicode.len(ap)+1 - b = a + unicode.len(bp)-1 - return a,b - else - return a - end -end - ---Умный текст по аналогии с майнчатовским. Ставишь символ параграфа, указываешь хуйню - и хуякс! Работает! -function ecs.smartText(x, y, text) - local sText = unicode.len(text) - local specialSymbol = "§" - --Разбираем по кусочкам строку и получаем цвета - local massiv = {} - local iterator = 1 - local currentColor = component.gpu.getForeground() - while iterator <= sText do - local symbol = unicode.sub(text, iterator, iterator) - if symbol == specialSymbol then - currentColor = ecs.colors[unicode.sub(text, iterator + 1, iterator + 1) or "f"] - iterator = iterator + 1 - else - table.insert(massiv, {symbol, currentColor}) - end - symbol = nil - iterator = iterator + 1 - end - x = x - 1 - for i = 1, #massiv do - if currentColor ~= massiv[i][2] then currentColor = massiv[i][2]; component.gpu.setForeground(massiv[i][2]) end - component.gpu.set(x + i, y, massiv[i][1]) - end -end - ---Аналог умного текста, но использующий HEX-цвета для кодировки -function ecs.formattedText(x, y, text, limit) - --Ограничение длины строки - limit = limit or math.huge - --Стартовая позиция курсора для отрисовки - local xPos = x - --Создаем массив символов данной строки - local symbols = {} - for i = 1, unicode.len(text) do table.insert(symbols, unicode.sub(text, i, i)) end - --Перебираем все символы строки, пока не переберем все или не достигнем указанного лимита - local i = 1 - while i <= #symbols and i <= limit do - --Если находим символ параграфа, то - if symbols[i] == "§" then - --Меняем цвет текста на указанный - component.gpu.setForeground(tonumber("0x" .. symbols[i+1] .. symbols[i+2] .. symbols[i+3] .. symbols[i+4] .. symbols[i+5] .. symbols[i+6])) - --Увеличиваем лимит на 7, т.к. - limit = limit + 7 - --Сдвигаем итератор цикла на 7 - i = i + 7 - end - --Рисуем символ на нужной позиции - component.gpu.set(xPos, y, symbols[i]) - --Увеличиваем позицию курсора и итератор на 1 - xPos = xPos + 1 - i = i + 1 - end -end - ---Инвертированный текст на основе цвета фона -function ecs.invertedText(x,y,symbol) - local info = {component.gpu.get(x,y)} - ecs.adaptiveText(x,y,symbol,ecs.invertColor(info[3])) -end - ---Адаптивное округление числа -function ecs.adaptiveRound(chislo) - local celaya,drobnaya = math.modf(chislo) - if drobnaya >= 0.5 then - return (celaya + 1) - else - return celaya - end -end - ---Округление до опред. кол-ва знаков после запятой -function ecs.round(num, idp) - local mult = 10^(idp or 0) - return math.floor(num * mult + 0.5) / mult -end - ---Обычный квадрат указанного цвета -function ecs.square(x,y,width,height,color) - component.gpu.setBackground(color) - component.gpu.fill(x,y,width,height," ") -end - ---Юникодовская рамка -function ecs.border(x, y, width, height, back, fore) - local stringUp = "┌"..string.rep("─", width - 2).."┐" - local stringDown = "└"..string.rep("─", width - 2).."┘" - component.gpu.setForeground(fore) - component.gpu.setBackground(back) - component.gpu.set(x, y, stringUp) - component.gpu.set(x, y + height - 1, stringDown) - - local yPos = 1 - for i = 1, (height - 2) do - component.gpu.set(x, y + yPos, "│") - component.gpu.set(x + width - 1, y + yPos, "│") - yPos = yPos + 1 - end -end - ---Кнопка в виде текста в рамке -function ecs.drawFramedButton(x, y, width, height, text, color) - ecs.border(x, y, width, height, component.gpu.getBackground(), color) - component.gpu.fill(x + 1, y + 1, width - 2, height - 2, " ") - x = x + math.floor(width / 2 - unicode.len(text) / 2) - y = y + math.floor(width / 2 - 1) - component.gpu.set(x, y, text) -end - ---Юникодовский разделитель -function ecs.separator(x, y, width, back, fore) - ecs.colorTextWithBack(x, y, fore, back, string.rep("─", width)) -end - ---Автоматическое центрирование текста по указанной координате (x, y, xy) -function ecs.centerText(mode,coord,text) - local dlina = unicode.len(text) - local xSize,ySize = component.gpu.getResolution() - - if mode == "x" then - component.gpu.set(math.floor(xSize/2-dlina/2),coord,text) - elseif mode == "y" then - component.gpu.set(coord,math.floor(ySize/2),text) - else - component.gpu.set(math.floor(xSize/2-dlina/2),math.floor(ySize/2),text) - end -end - ---Отрисовка "изображения" по указанному массиву -function ecs.drawCustomImage(x,y,pixels) - x = x - 1 - y = y - 1 - local pixelsWidth = #pixels[1] - local pixelsHeight = #pixels - local xEnd = x + pixelsWidth - local yEnd = y + pixelsHeight - - for i=1,pixelsHeight do - for j=1,pixelsWidth do - if pixels[i][j][3] ~= "#" then - if component.gpu.getBackground() ~= pixels[i][j][1] then component.gpu.setBackground(pixels[i][j][1]) end - if component.gpu.getForeground() ~= pixels[i][j][2] then component.gpu.setForeground(pixels[i][j][2]) end - component.gpu.set(x+j,y+i,pixels[i][j][3]) - end - end - end - - return (x+1),(y+1),xEnd,yEnd -end - ---Корректировка стартовых координат. Core-функция для всех моих программ -function ecs.correctStartCoords(xStart,yStart,xWindowSize,yWindowSize) - local xSize,ySize = component.gpu.getResolution() - if xStart == "auto" then - xStart = math.floor(xSize/2 - xWindowSize/2) - end - if yStart == "auto" then - yStart = math.ceil(ySize/2 - yWindowSize/2) - end - return xStart,yStart -end - ---Запомнить область пикселей и возвратить ее в виде массива -function ecs.rememberOldPixels(x, y, x2, y2) - local newPNGMassiv = { ["backgrounds"] = {} } - local xSize, ySize = component.gpu.getResolution() - newPNGMassiv.x, newPNGMassiv.y = x, y - - --Перебираем весь массив стандартного PNG-вида по высоте - local xCounter, yCounter = 1, 1 - for j = y, y2 do - xCounter = 1 - for i = x, x2 do - - if (i > xSize or i < 0) or (j > ySize or j < 0) then - error("Can't remember pixel, because it's located behind the screen: x("..i.."), y("..j..") out of xSize("..xSize.."), ySize("..ySize..")\n") - end - - local symbol, fore, back = component.gpu.get(i, j) - - newPNGMassiv["backgrounds"][back] = newPNGMassiv["backgrounds"][back] or {} - newPNGMassiv["backgrounds"][back][fore] = newPNGMassiv["backgrounds"][back][fore] or {} - - table.insert(newPNGMassiv["backgrounds"][back][fore], {xCounter, yCounter, symbol} ) - - xCounter = xCounter + 1 - back, fore, symbol = nil, nil, nil - end - - yCounter = yCounter + 1 - end - - xSize, ySize = nil, nil - return newPNGMassiv -end - ---Нарисовать запомненные ранее пиксели из массива -function ecs.drawOldPixels(massivSudaPihay) - --Перебираем массив с фонами - for back, backValue in pairs(massivSudaPihay["backgrounds"]) do - component.gpu.setBackground(back) - for fore, foreValue in pairs(massivSudaPihay["backgrounds"][back]) do - component.gpu.setForeground(fore) - for pixel = 1, #massivSudaPihay["backgrounds"][back][fore] do - if massivSudaPihay["backgrounds"][back][fore][pixel][3] ~= transparentSymbol then - component.gpu.set(massivSudaPihay.x + massivSudaPihay["backgrounds"][back][fore][pixel][1] - 1, massivSudaPihay.y + massivSudaPihay["backgrounds"][back][fore][pixel][2] - 1, massivSudaPihay["backgrounds"][back][fore][pixel][3]) - end - end - end - end -end - ---Ограничение длины строки. Маст-хев функция. -function ecs.stringLimit(mode, text, size, noDots) - if unicode.len(text) <= size then return text end - local length = unicode.len(text) - if mode == "start" then - if noDots then - return unicode.sub(text, length - size + 1, -1) - else - return "…" .. unicode.sub(text, length - size + 2, -1) - end - else - if noDots then - return unicode.sub(text, 1, size) - else - return unicode.sub(text, 1, size - 1) .. "…" - end - end -end - ---Получить текущее реальное время компьютера, хостящего сервер майна -function ecs.getHostTime(timezone) - timezone = timezone or 2 - --Создаем файл с записанной в него парашей - local file = io.open("HostTime.tmp", "w") - file:write("") - file:close() - --Коррекция времени на основе часового пояса - local timeCorrection = timezone * 3600 - --Получаем дату изменения файла в юникс-виде - local lastModified = tonumber(string.sub(fs.lastModified("HostTime.tmp"), 1, -4)) + timeCorrection - --Удаляем файл, ибо на хуй он нам не нужен - fs.remove("HostTime.tmp") - --Конвертируем юникс-время в норм время - local year, month, day, hour, minute, second = os.date("%Y", lastModified), os.date("%m", lastModified), os.date("%d", lastModified), os.date("%H", lastModified), os.date("%M", lastModified), os.date("%S", lastModified) - --Возвращаем все - return tonumber(day), tonumber(month), tonumber(year), tonumber(hour), tonumber(minute), tonumber(second) -end - ---Получить спискок файлов из конкретной директории, костыль -function ecs.getFileList(path) - local list = fs.list(path) - local massiv = {} - for file in list do - --if string.find(file, "%/$") then file = unicode.sub(file, 1, -2) end - table.insert(massiv, file) - end - list = nil - return massiv -end - ---Получить файловое древо. Сильно нагружает систему, только для дебага! -function ecs.getFileTree(path) - local massiv = {} - local list = ecs.getFileList(path) - for key, file in pairs(list) do - if fs.isDirectory(path.."/"..file) then - table.insert(massiv, getFileTree(path.."/"..file)) - else - table.insert(massiv, file) - end - end - list = nil - - return massiv -end - ---Поиск по файловой системе -function ecs.find(path, cheBudemIskat) - --Массив, в котором будут находиться все найденные соответствия - local massivNaydennogoGovna = {} - --Костыль, но удобный - local function dofind(path, cheBudemIskat) - --Получаем список файлов в директории - local list = ecs.getFileList(path) - --Перебираем все элементы файл листа - for key, file in pairs(list) do - --Путь к файлу - local pathToFile = path..file - --Если нашло совпадение в имени файла, то выдает путь к этому файлу - if string.find(unicode.lower(file), unicode.lower(cheBudemIskat)) then - table.insert(massivNaydennogoGovna, pathToFile) - end - --Анализ, что делать дальше - if fs.isDirectory(pathToFile) then - dofind(pathToFile, cheBudemIskat) - end - --Очищаем оперативку - pathToFile = nil - end - --Очищаем оперативку - list = nil - end - --Выполняем функцию - dofind(path, cheBudemIskat) - --Возвращаем, че нашло - return massivNaydennogoGovna -end - ---Получение формата файла -function ecs.getFileFormat(path) - local name = fs.name(path) - local starting, ending = string.find(name, "(.)%.[%d%w]*$") - if starting == nil then - return nil - else - return unicode.sub(name,starting + 1, -1) - end - name, starting, ending = nil, nil, nil -end - ---Проверить, скрытый ли файл (.пидор, .хуй = true; пидор, хуй = false) -function ecs.isFileHidden(path) - local name = fs.name(path) - local starting, ending = string.find(name, "^%.(.*)$") - if starting == nil then - return false - else - return true - end - name, starting, ending = nil, nil, nil -end - ---Скрыть формат файла -function ecs.hideFileFormat(path) - local name = fs.name(path) - local fileFormat = ecs.getFileFormat(name) - if fileFormat == nil then - return name - else - return unicode.sub(name, 1, unicode.len(name) - unicode.len(fileFormat)) - end -end - ---Ожидание клика либо нажатия какой-либо клавиши -function ecs.waitForTouchOrClick() - while true do - local e = { event.pull() } - if e[1] == "key_down" or e[1] == "touch" then break end - end -end - ---То же самое, но в сокращенном варианте -function ecs.wait() - ecs.waitForTouchOrClick() -end - ---Нарисовать кнопочки закрытия окна -function ecs.drawCloses(x, y, active) - local symbol = "⮾" - ecs.colorText(x, y , (active == 1 and ecs.colors.blue) or 0xCC4C4C, symbol) - ecs.colorText(x + 2, y , (active == 2 and ecs.colors.blue) or 0xDEDE6C, symbol) - ecs.colorText(x + 4, y , (active == 3 and ecs.colors.blue) or 0x57A64E, symbol) -end - ---Нарисовать верхнюю оконную панель с выбором объектов -function ecs.drawTopBar(x, y, width, selectedObject, background, foreground, ...) - local objects = { ... } - ecs.square(x, y, width, 3, background) - local widthOfObjects = 0 - local spaceBetween = 2 - for i = 1, #objects do - widthOfObjects = widthOfObjects + unicode.len(objects[i][1]) + spaceBetween - end - local xPos = x + math.floor(width / 2 - widthOfObjects / 2) - for i = 1, #objects do - if i == selectedObject then - ecs.square(xPos, y, unicode.len(objects[i][1]) + spaceBetween, 3, ecs.colors.blue) - component.gpu.setForeground(0xffffff) - else - component.gpu.setBackground(background) - component.gpu.setForeground(foreground) - end - component.gpu.set(xPos + spaceBetween / 2, y + 2, objects[i][1]) - component.gpu.set(xPos + math.ceil(unicode.len(objects[i][1]) / 2), y + 1, objects[i][2]) - - xPos = xPos + unicode.len(objects[i][1]) + spaceBetween - end -end - ---Нарисовать топ-меню, горизонтальная полоска такая с текстами -function ecs.drawTopMenu(x, y, width, color, selectedObject, ...) - local objects = { ... } - local objectsToReturn = {} - local xPos = x + 2 - local spaceBetween = 2 - ecs.square(x, y, width, 1, color) - for i = 1, #objects do - if i == selectedObject then - ecs.square(xPos - 1, y, unicode.len(objects[i][1]) + spaceBetween, 1, ecs.colors.blue) - component.gpu.setForeground(0xffffff) - component.gpu.set(xPos, y, objects[i][1]) - component.gpu.setForeground(objects[i][2]) - component.gpu.setBackground(color) - else - if component.gpu.getForeground() ~= objects[i][2] then component.gpu.setForeground(objects[i][2]) end - component.gpu.set(xPos, y, objects[i][1]) - end - objectsToReturn[objects[i][1]] = { xPos, y, xPos + unicode.len(objects[i][1]) - 1, y, i } - xPos = xPos + unicode.len(objects[i][1]) + spaceBetween - end - return objectsToReturn -end - ---Функция отрисовки кнопки указанной ширины -function ecs.drawButton(x,y,width,height,text,backColor,textColor) - x,y = ecs.correctStartCoords(x,y,width,height) - - local textPosX = math.floor(x + width / 2 - unicode.len(text) / 2) - local textPosY = math.floor(y + height / 2) - ecs.square(x,y,width,height,backColor) - ecs.colorText(textPosX,textPosY,textColor,text) - - return x, y, (x + width - 1), (y + height - 1) -end - ---Отрисовка кнопки с указанными отступами от текста -function ecs.drawAdaptiveButton(x,y,offsetX,offsetY,text,backColor,textColor) - local length = unicode.len(text) - local width = offsetX*2 + length - local height = offsetY*2 + 1 - - x,y = ecs.correctStartCoords(x,y,width,height) - - ecs.square(x,y,width,height,backColor) - ecs.colorText(x+offsetX,y+offsetY,textColor,text) - - return x,y,(x+width-1),(y+height-1) -end - ---Отрисовка оконной "тени" -function ecs.windowShadow(x,y,width,height) - component.gpu.setBackground(ecs.windowColors.shadow) - component.gpu.fill(x+width,y+1,2,height," ") - component.gpu.fill(x+1,y+height,width,1," ") -end - ---Просто белое окошко с тенью -function ecs.blankWindow(x,y,width,height) - local oldPixels = ecs.rememberOldPixels(x,y,x+width+1,y+height) - - ecs.square(x,y,width,height,ecs.windowColors.background) - - ecs.windowShadow(x,y,width,height) - - return oldPixels -end - ---Белое окошко, но уже с титлом вверху! -function ecs.emptyWindow(x,y,width,height,title) - - local oldPixels = ecs.rememberOldPixels(x,y,x+width+1,y+height) - - --ОКНО - component.gpu.setBackground(ecs.windowColors.background) - component.gpu.fill(x,y+1,width,height-1," ") - - --ТАБ СВЕРХУ - component.gpu.setBackground(ecs.windowColors.tab) - component.gpu.fill(x,y,width,1," ") - - --ТИТЛ - component.gpu.setForeground(ecs.windowColors.title) - local textPosX = x + math.floor(width/2-unicode.len(title)/2) -1 - component.gpu.set(textPosX,y,title) - - --ТЕНЬ - ecs.windowShadow(x,y,width,height) - - return oldPixels - -end - -function ecs.getWordsArrayFromString(s) - local words = {} - for word in string.gmatch(s, "[^%s]+") do table.insert(words, word) end - return words -end - ---Моя любимая функция ошибки C: -function ecs.error(...) - local args = {...} - local text = "" - if #args > 1 then - for i = 1, #args do - --text = text .. "[" .. i .. "] = " .. tostring(args[i]) - if type(args[i]) == "string" then args[i] = "\"" .. args[i] .. "\"" end - text = text .. tostring(args[i]) - if i ~= #args then text = text .. ", " end - end - else - text = tostring(args[1]) - end - ecs.universalWindow("auto", "auto", math.ceil(component.gpu.getResolution() * 0.45), ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x880000, "Ошибка!"}, {"EmptyLine"}, {"WrappedText", 0x262626, text}, {"EmptyLine"}, {"Button", {0x880000, 0xffffff, "OK!"}}) -end - ---Очистить экран, установить комфортные цвета и поставить курсок на 1, 1 -function ecs.prepareToExit(color1, color2) - term.setCursor(1, 1) - ecs.clearScreen(color1 or 0x333333) - component.gpu.setForeground(color2 or 0xffffff) - component.gpu.set(1, 1, "") -end - ---Конвертация из юникода в символ. Вроде норм, а вроде и не норм. Но полезно. -function ecs.convertCodeToSymbol(code) - local symbol - if code ~= 0 and code ~= 13 and code ~= 8 and code ~= 9 and code ~= 200 and code ~= 208 and code ~= 203 and code ~= 205 and not keyboard.isControlDown() then - symbol = unicode.char(code) - if keyboard.isShiftPressed then symbol = unicode.upper(symbol) end - end - return symbol -end - ---Шкала прогресса - маст-хев! -function ecs.progressBar(x, y, width, height, background, foreground, percent) - local activeWidth = math.ceil(width * percent / 100) - ecs.square(x, y, width, height, background) - ecs.square(x, y, activeWidth, height, foreground) -end - ---Окошко с прогрессбаром! Давно хотел -function ecs.progressWindow(x, y, width, percent, text, returnOldPixels) - local height = 6 - local barWidth = width - 6 - - x, y = ecs.correctStartCoords(x, y, width, height) - - local oldPixels - if returnOldPixels then - oldPixels = ecs.rememberOldPixels(x, y, x + width + 1, y + height) - end - - ecs.emptyWindow(x, y, width, height, " ") - ecs.colorTextWithBack(x + math.floor(width / 2 - unicode.len(text) / 2), y + 4, 0x000000, ecs.windowColors.background, text) - ecs.progressBar(x + 3, y + 2, barWidth, 1, 0xCCCCCC, ecs.colors.blue, percent) - - return oldPixels -end - ---Функция для ввода текста в мини-поле. -function ecs.inputText(x, y, limit, cheBiloVvedeno, background, foreground, justDrawNotEvent, maskTextWith) - limit = limit or 10 - cheBiloVvedeno = cheBiloVvedeno or "" - background = background or 0xffffff - foreground = foreground or 0x000000 - - component.gpu.setBackground(background) - component.gpu.setForeground(foreground) - component.gpu.fill(x, y, limit, 1, " ") - - local text = cheBiloVvedeno - - local function draw() - term.setCursorBlink(false) - - local dlina = unicode.len(text) - local xCursor = x + dlina - if xCursor > (x + limit - 1) then xCursor = (x + limit - 1) end - - if maskTextWith then - component.gpu.set(x, y, ecs.stringLimit("start", string.rep("●", dlina), limit)) - else - component.gpu.set(x, y, ecs.stringLimit("start", text, limit)) - end - - term.setCursor(xCursor, y) - - term.setCursorBlink(true) - end - - draw() - - if justDrawNotEvent then term.setCursorBlink(false); return cheBiloVvedeno end - - while true do - local e = {event.pull()} - if e[1] == "key_down" then - if e[4] == 14 then - term.setCursorBlink(false) - text = unicode.sub(text, 1, -2) - if unicode.len(text) < limit then component.gpu.set(x + unicode.len(text), y, " ") end - draw() - elseif e[4] == 28 then - term.setCursorBlink(false) - return text - else - local symbol = ecs.convertCodeToSymbol(e[3]) - if symbol then - text = text..symbol - draw() - end - end - elseif e[1] == "touch" then - term.setCursorBlink(false) - return text - elseif e[1] == "clipboard" then - if e[3] then - text = text..e[3] - draw() - end - end - end -end - ---Спросить, заменять ли файл (если таковой уже имеется) -function ecs.askForReplaceFile(path, includeForAllButton) - local cyka = { - {"EmptyLine"}, - {"CenterText", 0x262626, "Файл \"".. fs.name(path) .. "\" уже имеется в этом месте."}, - {"CenterText", 0x262626, "Заменить его перемещаемым объектом?"}, - {"EmptyLine"} - } - if includeForAllButton then table.insert(cyka, {"Switch", 0xF2B233, 0xffffff, 0x262626, "Для всех", true}) end - table.insert(cyka, {"Button", {0x444444, 0xFFFFFF, "Заменить"}, {0x666666, 0xFFFFFF, "Отмена"}}) - - local action = ecs.universalWindow("auto", "auto", 46, ecs.windowColors.background, true, table.unpack(cyka)) - if action[includeForAllButton and 2 or 1] == "Заменить" then - return 1, action[includeForAllButton and 1] - else - return 2, action[includeForAllButton and 1] - end -end - ---Проверить имя файла на соответствие критериям -function ecs.checkName(name, path) - --Если ввели хуйню какую-то, то - if name == "" or name == " " or name == nil then - ecs.error("Неверное имя файла.") - return false - else - --Если файл с новым путем уже существует, то - if fs.exists(path .. name) then - ecs.error("Файл \"".. name .. "\" уже имеется в этом месте.") - return false - --А если все заебок, то - else - return true - end - end -end - ---Переименование файлов (для операционки) -function ecs.rename(mainPath) - --Задаем стартовую щнягу - local name = fs.name(mainPath) - path = fs.path(mainPath) - --Рисуем окошко ввода нового имени файла - local inputs = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Переименовать"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, name}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}}) - --Переименовываем - if ecs.checkName(inputs[1], path) then - fs.rename(mainPath, path .. inputs[1]) - end -end - ---Создать новую папку (для операционки) -function ecs.newFolder(path) - --Рисуем окошко ввода нового имени файла - local inputs = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Новая папка"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, ""}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}}) - - if ecs.checkName(inputs[1], path) then - fs.makeDirectory(path .. inputs[1]) - end -end - ---Создать новый файл (для операционки) -function ecs.newFile(path) - --Рисуем окошко ввода нового имени файла - local inputs = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Новый файл"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, ""}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}}) - - if ecs.checkName(inputs[1], path) then - local MineOSCore = require("MineOSCore") - MineOSCore.safeLaunch(MineOSCore.paths.applications .. "/MineCode IDE.app/Main.lua", "open", path .. inputs[1]) - end -end - ---Создать новое приложение (для операционки) -function ecs.newApplication(path, startName) - --Рисуем окошко ввода нового имени файла - local inputs - if not startName then - inputs = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Новое приложение"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Введите имя"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}}) - end - - if ecs.checkName(inputs[1] .. ".app", path) then - local name = path .. inputs[1] .. ".app/Resources/" - fs.makeDirectory(name) - fs.copy("MineOS/System/OS/Icons/SampleIcon.pic", name .. "Icon.pic") - local file = io.open(path .. inputs[1] .. ".app/Main.lua", "w") - file:write("require('GUI').error('Hello world')") - file:close() - end -end - ---Создать приложение на основе существующего ЛУА-файла -function ecs.newApplicationFromLuaFile(pathToLuaFile, pathWhereToCreateApplication) - local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x000000, "Новое приложение"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Имя приложения"}, {"Input", 0x262626, 0x880000, "Путь к иконке приложения"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}}) - data[1] = data[1] or "MyApplication" - data[2] = data[2] or "MineOS/System/OS/Icons/SampleIcon.pic" - if fs.exists(data[2]) then - fs.makeDirectory(pathWhereToCreateApplication .. "/" .. data[1] .. ".app/Resources") - fs.copy(pathToLuaFile, pathWhereToCreateApplication .. "/" .. data[1] .. ".app/" .. data[1] .. ".lua") - fs.copy(data[2], pathWhereToCreateApplication .. "/" .. data[1] .. ".app/Resources/Icon.pic") - - --ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x000000, "Приложение создано!"}, {"EmptyLine"}, {"Button", {ecs.colors.green, 0xffffff, "OK"}}) - else - ecs.error("Указанный файл иконки не существует.") - return - end -end - ---Простое информационное окошечко. Возвращает старые пиксели - мало ли понадобится. -function ecs.info(x, y, title, text) - x = x or "auto" - y = y or "auto" - title = title or " " - text = text or "Sample text" - - local width = unicode.len(text) + 4 - local height = 4 - x, y = ecs.correctStartCoords(x, y, width, height) - - local oldPixels = ecs.rememberOldPixels(x, y, x + width + 1, y + height) - - ecs.emptyWindow(x, y, width, height, title) - ecs.colorTextWithBack(x + 2, y + 2, ecs.windowColors.usualText, ecs.windowColors.background, text) - - return oldPixels -end - ---Вертикальный скроллбар. Маст-хев! -function ecs.srollBar(x, y, width, height, countOfAllElements, currentElement, backColor, frontColor) - local sizeOfScrollBar = math.ceil(1 / countOfAllElements * height) - local displayBarFrom = math.floor(y + height * ((currentElement - 1) / countOfAllElements)) - - ecs.square(x, y, width, height, backColor) - ecs.square(x, displayBarFrom, width, sizeOfScrollBar, frontColor) - - sizeOfScrollBar, displayBarFrom = nil, nil -end - ---Отрисовка поля с текстом. Сюда пихать массив вида {"строка1", "строка2", "строка3", ...} -function ecs.textField(x, y, width, height, lines, displayFrom, background, foreground, scrollbarBackground, scrollbarForeground) - x, y = ecs.correctStartCoords(x, y, width, height) - - background = background or 0xffffff - foreground = foreground or ecs.windowColors.usualText - - local sLines = #lines - local lineLimit = width - 3 - - --Парсим строки - local line = 1 - while lines[line] do - local sLine = unicode.len(lines[line]) - if sLine > lineLimit then - local part1, part2 = unicode.sub(lines[line], 1, lineLimit), unicode.sub(lines[line], lineLimit + 1, -1) - lines[line] = part1 - table.insert(lines, line + 1, part2) - part1, part2 = nil, nil - end - line = line + 1 - sLine = nil - end - line = nil - - ecs.square(x, y, width - 1, height, background) - ecs.srollBar(x + width - 1, y, 1, height, sLines, displayFrom, scrollbarBackground, scrollbarForeground) - - component.gpu.setBackground(background) - component.gpu.setForeground(foreground) - local yPos = y - for i = displayFrom, (displayFrom + height - 1) do - if lines[i] then - component.gpu.set(x + 1, yPos, lines[i]) - yPos = yPos + 1 - else - break - end - end - - return sLines -end - ---Получение верного имени языка. Просто для безопасности. (для операционки) -function ecs.getCorrectLangName(pathToLangs) - local language = _G.OSSettings.language .. ".lang" - if not fs.exists(pathToLangs .. "/" .. language) then - language = "English.lang" - end - return language -end - ---Чтение языкового файла (для операционки) -function ecs.readCorrectLangFile(pathToLangs) - local lang - - local language = ecs.getCorrectLangName(pathToLangs) - - lang = config.readAll(pathToLangs .. "/" .. language) - - return lang -end - --------------------------ВСЕ ДЛЯ ОСКИ------------------------------------------------------------------------------- - -function ecs.searchInArray(array, textToSearch) - local newArray = {} - for i = 1, #array do - if string.find(unicode.lower(array[i]), unicode.lower(textToSearch)) then table.insert(newArray, array[i]) end - end - return newArray -end - -function ecs.sortFiles(path, fileList, sortingMethod, showHiddenFiles) - local sortedFileList = {} - if sortingMethod == "type" or sortingMethod == 0 then - local typeList = {} - for i = 1, #fileList do - local fileFormat = ecs.getFileFormat(fileList[i]) or "Script" - if fs.isDirectory(path .. fileList[i]) and fileFormat ~= ".app" then fileFormat = "Folder" end - typeList[fileFormat] = typeList[fileFormat] or {} - table.insert(typeList[fileFormat], fileList[i]) - end - - if typeList["Folder"] then - for i = 1, #typeList["Folder"] do - table.insert(sortedFileList, typeList["Folder"][i]) - end - typeList["Folder"] = nil - end - - for fileFormat in pairs(typeList) do - for i = 1, #typeList[fileFormat] do - table.insert(sortedFileList, typeList[fileFormat][i]) - end - end - elseif sortingMethod == "name" or sortingMethod == 1 then - sortedFileList = fileList - elseif sortingMethod == "date" or sortingMethod == 2 then - for i = 1, #fileList do - fileList[i] = {fileList[i], fs.lastModified(path .. fileList[i])} - end - table.sort(fileList, function(a,b) return a[2] > b[2] end) - for i = 1, #fileList do - table.insert(sortedFileList, fileList[i][1]) - end - else - error("Unknown sorting method: " .. tostring(sortingMethod)) - end - - local i = 1 - while i <= #sortedFileList do - if not showHiddenFiles and ecs.isFileHidden(sortedFileList[i]) then - table.remove(sortedFileList, i) - else - i = i + 1 - end - end - - return sortedFileList -end - ---Сохранить файл конфигурации ОС -function ecs.saveOSSettings() - local pathToOSSettings = "MineOS/System/OS/OSSettings.cfg" - if not _G.OSSettings then error("Массив настроек ОС отсутствует в памяти!") end - fs.makeDirectory(fs.path(pathToOSSettings)) - local file = io.open(pathToOSSettings, "w") - file:write(serialization.serialize(_G.OSSettings)) - file:close() -end - ---Загрузить файл конфигурации ОС, а если его не существует, то создать -function ecs.loadOSSettings() - local pathToOSSettings = "MineOS/System/OS/OSSettings.cfg" - if fs.exists(pathToOSSettings) then - local file = io.open(pathToOSSettings, "r") - _G.OSSettings = serialization.unserialize(file:read("*a")) - file:close() - else - _G.OSSettings = { showHelpOnApplicationStart = true, language = "Russian" } - ecs.saveOSSettings() - end -end - ---Отобразить окно с содержимым файла информации о приложении -function ecs.applicationHelp(pathToApplication) - local pathToAboutFile = pathToApplication .. "/resources/About/" .. _G.OSSettings.language .. ".txt" - if _G.OSSettings and _G.OSSettings.showHelpOnApplicationStart and fs.exists(pathToAboutFile) then - local applicationName = fs.name(pathToApplication) - local file = io.open(pathToAboutFile, "r") - local text = "" - for line in file:lines() do text = text .. line .. " " end - file:close() - - local data = ecs.universalWindow("auto", "auto", 30, 0xeeeeee, true, - {"EmptyLine"}, - {"CenterText", 0x000000, "О приложении " .. applicationName}, - {"EmptyLine"}, - {"TextField", 16, 0xFFFFFF, 0x262626, 0xcccccc, 0x353535, text}, - {"EmptyLine"}, - {"Button", {ecs.colors.orange, 0x262626, "OK"}, {0x999999, 0xffffff, "Больше не показывать"}} - ) - if data[1] ~= "OK" then - _G.OSSettings.showHelpOnApplicationStart = false - ecs.saveOSSettings() - end - end -end - -function ecs.correctFileNameIfFileExists(path, requestedName) - local number = 1 - local fileFormat = ecs.getFileFormat(requestedName) or "" - requestedName = ecs.hideFileFormat(requestedName) - while true do - local finalFileName = requestedName .. string.rep("-copy", number) .. fileFormat - if fs.exists(path .. "/" .. finalFileName) then - number = number + 1 - else - return finalFileName - end - end -end - ---Создать ярлык для конкретной проги (для операционки) -function ecs.createShortCut(pathToShortcut, pathToFile) - local pathToPathToShortcut = fs.path(pathToShortcut) or "/" - fs.makeDirectory(pathToPathToShortcut) - if fs.exists(pathToShortcut) then - pathToShortcut = ecs.correctFileNameIfFileExists(pathToPathToShortcut, pathToShortcut) - end - - local file = io.open(pathToShortcut, "w") - file:write("return ", "\"", pathToFile, "\"") - file:close() -end - ---Получить данные о файле из ярлыка (для операционки) -function ecs.readShortcut(path) - local success, filename = pcall(loadfile(path)) - if success then - return filename - else - error("Ошибка чтения файла ярлыка. Вероятно, он создан криво, либо не существует в папке \"" .. fs.path(path) or "" .. "\"") - end -end - ---Редактирование файла (для операционки) -function ecs.editFile(path) - ecs.prepareToExit() - shell.execute("edit " .. path) -end - ---Копирование файлов и папок -function ecs.copy(from, toFolder) - fs.makeDirectory(toFolder) - - if fs.isDirectory(from) then - local currentAction, yesToAllAction - local function recursiveFolderCopy(from, to) - for file in fs.list(from) do - local finalFromName = from .. "/" .. file - local finalToName = to .. "/" .. file - - if fs.exists(finalToName) then - if not yesToAllAction then - currentAction, yesToAll = ecs.askForReplaceFile(finalToName, true) - if yesToAll == true then yesToAllAction = true end - end - else - currentAction = 1 - end - - if currentAction == 1 then - if fs.isDirectory(finalFromName) then - fs.makeDirectory(finalToName) - recursiveFolderCopy(finalFromName, finalToName) - else - fs.copy(finalFromName, finalToName) - end - end - end - end - - recursiveFolderCopy(from, toFolder .. fs.name(from)) - else - local to = toFolder .. "/" .. fs.name(from) - local action = 1 - if fs.exists(to) then action = ecs.askForReplaceFile(to) end - if action == 1 then fs.copy(from, to) end - end -end - --- Анимация затухания экрана -function ecs.fadeOut(startColor, targetColor, speed) - local xSize, ySize = component.gpu.getResolution() - while startColor >= targetColor do - component.gpu.setBackground(startColor) - component.gpu.fill(1, 1, xSize, ySize, " ") - startColor = startColor - 0x111111 - os.sleep(speed or 0) - end -end - --- Анимация загорания экрана -function ecs.fadeIn(startColor, targetColor, speed) - local xSize, ySize = component.gpu.getResolution() - while startColor <= targetColor do - component.gpu.setBackground(startColor) - component.gpu.fill(1, 1, xSize, ySize, " ") - startColor = startColor + 0x111111 - os.sleep(speed or 0) - end -end - --- Анимация выхода в олдскул-телевизионном стиле -function ecs.TV(speed, targetColor) - local xSize, ySize = component.gpu.getResolution() - local xCenter, yCenter = math.floor(xSize / 2), math.floor(ySize / 2) - component.gpu.setBackground(targetColor or 0x000000) - - for y = 1, yCenter do - component.gpu.fill(1, y - 1, xSize, 1, " ") - component.gpu.fill(1, ySize - y + 1, xSize, 1, " ") - os.sleep(speed or 0) - end - - for x = 1, xCenter - 1 do - component.gpu.fill(x, yCenter, 1, 1, " ") - component.gpu.fill(xSize - x + 1, yCenter, 1, 1, " ") - os.sleep(speed or 0) - end - os.sleep(0.3) - component.gpu.fill(1, yCenter, xSize, 1, " ") -end - - - ----------------------------------------------ОКОШЕЧКИ------------------------------------------------------------ - - ---Описание ниже, ебана. Ниже - это значит в самой жопе кода! -function ecs.universalWindow(x, y, width, background, closeWindowAfter, ...) - local objects = {...} - local countOfObjects = #objects - - local pressedButton - local pressedMultiButton - - --Задаем высотные константы для объектов - local objectsHeights = { - ["button"] = 3, - ["centertext"] = 1, - ["emptyline"] = 1, - ["input"] = 3, - ["slider"] = 3, - ["select"] = 3, - ["selector"] = 3, - ["separator"] = 1, - ["switch"] = 1, - ["color"] = 3, - } - - --Скорректировать ширину, если нужно - local function correctWidth(newWidthForAnalyse) - width = math.max(width, newWidthForAnalyse) - end - - --Корректируем ширину - for i = 1, countOfObjects do - local objectType = string.lower(objects[i][1]) - - if objectType == "centertext" then - correctWidth(unicode.len(objects[i][3]) + 2) - elseif objectType == "slider" then --!!!!!!!!!!!!!!!!!! ВОТ ТУТ НЕ ЗАБУДЬ ФИКСАНУТЬ - correctWidth(unicode.len(objects[i][7]..tostring(objects[i][5].." ")) + 2) - elseif objectType == "select" then - for j = 4, #objects[i] do - correctWidth(unicode.len(objects[i][j]) + 2) - end - --elseif objectType == "selector" then - - --elseif objectType == "separator" then - - elseif objectType == "textfield" then - correctWidth(5) - elseif objectType == "wrappedtext" then - correctWidth(6) - elseif objectType == "button" then - --Корректируем ширину - local widthOfButtons = 0 - local maxButton = 0 - for j = 2, #objects[i] do - maxButton = math.max(maxButton, unicode.len(objects[i][j][3]) + 2) - end - widthOfButtons = maxButton * #objects[i] - correctWidth(widthOfButtons) - elseif objectType == "switch" then - local dlina = unicode.len(objects[i][5]) + 2 + 10 + 4 - correctWidth(dlina) - elseif objectType == "color" then - correctWidth(unicode.len(objects[i][2]) + 6) - end - end - - --Считаем высоту этой хуйни - local height = 0 - for i = 1, countOfObjects do - local objectType = string.lower(objects[i][1]) - if objectType == "select" then - height = height + (objectsHeights[objectType] * (#objects[i] - 3)) - elseif objectType == "textfield" then - height = height + objects[i][2] - elseif objectType == "wrappedtext" then - --Заранее парсим текст перенесенный - objects[i].wrapped = string.wrap({objects[i][3]}, width - 4) - objects[i].height = #objects[i].wrapped - height = height + objects[i].height - else - height = height + objectsHeights[objectType] - end - end - - --Коорректируем стартовые координаты - x, y = ecs.correctStartCoords(x, y, width, height) - --Запоминаем инфу о том, что было нарисовано, если это необходимо - local oldPixels, oldBackground, oldForeground - if closeWindowAfter then - oldBackground = component.gpu.getBackground() - oldForeground = component.gpu.getForeground() - oldPixels = ecs.rememberOldPixels(x, y, x + width - 1, y + height - 1) - end - --Считаем все координаты объектов - objects[1].y = y - if countOfObjects > 1 then - for i = 2, countOfObjects do - local objectType = string.lower(objects[i - 1][1]) - if objectType == "select" then - objects[i].y = objects[i - 1].y + (objectsHeights[objectType] * (#objects[i - 1] - 3)) - elseif objectType == "textfield" then - objects[i].y = objects[i - 1].y + objects[i - 1][2] - elseif objectType == "wrappedtext" then - objects[i].y = objects[i - 1].y + objects[i - 1].height - else - objects[i].y = objects[i - 1].y + objectsHeights[objectType] - end - end - end - - --Объекты для тача - local obj = {} - local function newObj(class, name, ...) - obj[class] = obj[class] or {} - obj[class][name] = {...} - end - - --Отображение объекта по номеру - local function displayObject(number, active) - local objectType = string.lower(objects[number][1]) - - if objectType == "centertext" then - local xPos = x + math.floor(width / 2 - unicode.len(objects[number][3]) / 2) - component.gpu.setForeground(objects[number][2]) - component.gpu.setBackground(background) - component.gpu.set(xPos, objects[number].y, objects[number][3]) - - elseif objectType == "input" then - - if active then - --Рамочка - ecs.border(x + 1, objects[number].y, width - 2, objectsHeights.input, background, objects[number][3]) - --Тестик - objects[number][4] = ecs.inputText(x + 3, objects[number].y + 1, width - 6, "", background, objects[number][3], false, objects[number][5]) - else - --Рамочка - ecs.border(x + 1, objects[number].y, width - 2, objectsHeights.input, background, objects[number][2]) - --Текстик - component.gpu.set(x + 3, objects[number].y + 1, ecs.stringLimit("start", objects[number][4], width - 6)) - ecs.inputText(x + 3, objects[number].y + 1, width - 6, objects[number][4], background, objects[number][2], true, objects[number][5]) - end - - newObj("Inputs", number, x + 1, objects[number].y, x + width - 2, objects[number].y + 2) - - elseif objectType == "slider" then - local widthOfSlider = width - 2 - local xOfSlider = x + 1 - local yOfSlider = objects[number].y + 1 - local countOfSliderThings = objects[number][5] - objects[number][4] - local showSliderValue= objects[number][7] - - local dolya = widthOfSlider / countOfSliderThings - local position = math.floor(dolya * objects[number][6]) - --Костыль - if (xOfSlider + position) > (xOfSlider + widthOfSlider - 1) then position = widthOfSlider - 2 end - - --Две линии - ecs.separator(xOfSlider, yOfSlider, position, background, objects[number][3]) - ecs.separator(xOfSlider + position, yOfSlider, widthOfSlider - position, background, objects[number][2]) - --Слудир - ecs.square(xOfSlider + position, yOfSlider, 2, 1, objects[number][3]) - - --Текстик под слудиром - if showSliderValue then - local text = showSliderValue .. tostring(objects[number][6]) .. (objects[number][8] or "") - local textPos = (xOfSlider + widthOfSlider / 2 - unicode.len(text) / 2) - ecs.square(x, yOfSlider + 1, width, 1, background) - ecs.colorText(textPos, yOfSlider + 1, objects[number][2], text) - end - - newObj("Sliders", number, xOfSlider, yOfSlider, x + widthOfSlider, yOfSlider, dolya) - - elseif objectType == "select" then - local usualColor = objects[number][2] - local selectionColor = objects[number][3] - - objects[number].selectedData = objects[number].selectedData or 1 - - local symbol = "✔" - local yPos = objects[number].y - for i = 4, #objects[number] do - --Коробка для галочки - ecs.border(x + 1, yPos, 5, 3, background, usualColor) - --Текст - component.gpu.set(x + 7, yPos + 1, objects[number][i]) - --Галочка - if objects[number].selectedData == (i - 3) then - ecs.colorText(x + 3, yPos + 1, selectionColor, symbol) - else - component.gpu.set(x + 3, yPos + 1, " ") - end - - obj["Selects"] = obj["Selects"] or {} - obj["Selects"][number] = obj["Selects"][number] or {} - obj["Selects"][number][i - 3] = { x + 1, yPos, x + width - 2, yPos + 2 } - - yPos = yPos + objectsHeights.select - end - - elseif objectType == "selector" then - local borderColor = objects[number][2] - local arrowColor = objects[number][3] - local selectorWidth = width - 2 - objects[number].selectedElement = objects[number].selectedElement or objects[number][4] - - local topLine = "┌" .. string.rep("─", selectorWidth - 6) .. "┬───┐" - local midLine = "│" .. string.rep(" ", selectorWidth - 6) .. "│ │" - local botLine = "└" .. string.rep("─", selectorWidth - 6) .. "┴───┘" - - local yPos = objects[number].y - - local function bordak(borderColor) - component.gpu.setBackground(background) - component.gpu.setForeground(borderColor) - component.gpu.set(x + 1, objects[number].y, topLine) - component.gpu.set(x + 1, objects[number].y + 1, midLine) - component.gpu.set(x + 1, objects[number].y + 2, botLine) - component.gpu.set(x + 3, objects[number].y + 1, ecs.stringLimit("start", objects[number].selectedElement, width - 6)) - ecs.colorText(x + width - 4, objects[number].y + 1, arrowColor, "▼") - end - - bordak(borderColor) - - --Выпадающий список, самый гемор, блядь - if active then - local xPos, yPos = x + 1, objects[number].y + 3 - local spisokWidth = width - 2 - local countOfElements = #objects[number] - 3 - local spisokHeight = countOfElements + 1 - local oldPixels = ecs.rememberOldPixels( xPos, yPos, xPos + spisokWidth - 1, yPos + spisokHeight - 1) - - local coords = {} - - bordak(arrowColor) - - --Рамку рисуем поверх фоника - local topLine = "├"..string.rep("─", spisokWidth - 6).."┴───┤" - local midLine = "│"..string.rep(" ", spisokWidth - 2).."│" - local botLine = "└"..string.rep("─", selectorWidth - 2) .. "┘" - ecs.colorTextWithBack(xPos, yPos - 1, arrowColor, background, topLine) - for i = 1, spisokHeight - 1 do - component.gpu.set(xPos, yPos + i - 1, midLine) - end - component.gpu.set(xPos, yPos + spisokHeight - 1, botLine) - - --Элементы рисуем - xPos = xPos + 2 - for i = 1, countOfElements do - ecs.colorText(xPos, yPos, borderColor, ecs.stringLimit("start", objects[number][i + 3], spisokWidth - 4)) - coords[i] = {xPos - 1, yPos, xPos + spisokWidth - 4, yPos} - yPos = yPos + 1 - end - - --Обработка - local exit - while true do - if exit then break end - local e = {event.pull()} - if e[1] == "touch" then - for i = 1, #coords do - if ecs.clickedAtArea(e[3], e[4], coords[i][1], coords[i][2], coords[i][3], coords[i][4]) then - ecs.square(coords[i][1], coords[i][2], spisokWidth - 2, 1, ecs.colors.blue) - ecs.colorText(coords[i][1] + 1, coords[i][2], 0xffffff, objects[number][i + 3]) - os.sleep(0.3) - objects[number].selectedElement = objects[number][i + 3] - exit = true - break - end - end - end - end - - ecs.drawOldPixels(oldPixels) - end - - newObj("Selectors", number, x + 1, objects[number].y, x + width - 2, objects[number].y + 2) - - elseif objectType == "separator" then - ecs.separator(x, objects[number].y, width, background, objects[number][2]) - - elseif objectType == "textfield" then - newObj("TextFields", number, x + 1, objects[number].y, x + width - 2, objects[number].y + objects[number][2] - 1) - if not objects[number].strings then objects[number].strings = string.wrap({objects[number][7]}, width - 3) end - objects[number].displayFrom = objects[number].displayFrom or 1 - ecs.textField(x, objects[number].y, width, objects[number][2], objects[number].strings, objects[number].displayFrom, objects[number][3], objects[number][4], objects[number][5], objects[number][6]) - - elseif objectType == "wrappedtext" then - component.gpu.setBackground(background) - component.gpu.setForeground(objects[number][2]) - for i = 1, #objects[number].wrapped do - component.gpu.set(x + 2, objects[number].y + i - 1, objects[number].wrapped[i]) - end - - elseif objectType == "button" then - - obj["MultiButtons"] = obj["MultiButtons"] or {} - obj["MultiButtons"][number] = {} - - local widthOfButton = math.floor(width / (#objects[number] - 1)) - - local xPos, yPos = x, objects[number].y - for i = 1, #objects[number] do - if type(objects[number][i]) == "table" then - local x1, y1, x2, y2 = ecs.drawButton(xPos, yPos, widthOfButton, 3, objects[number][i][3], objects[number][i][1], objects[number][i][2]) - table.insert(obj["MultiButtons"][number], {x1, y1, x2, y2, widthOfButton}) - xPos = x2 + 1 - - if i == #objects[number] then - ecs.square(xPos, yPos, x + width - xPos, 3, objects[number][i][1]) - obj["MultiButtons"][number][i - 1][5] = obj["MultiButtons"][number][i - 1][5] + x + width - xPos - end - - x1, y1, x2, y2 = nil, nil, nil, nil - end - end - - elseif objectType == "switch" then - - local xPos, yPos = x + 2, objects[number].y - local activeColor, passiveColor, textColor, text, state = objects[number][2], objects[number][3], objects[number][4], objects[number][5], objects[number][6] - local switchWidth = 8 - ecs.colorTextWithBack(xPos, yPos, textColor, background, text) - - xPos = x + width - switchWidth - 2 - if state then - ecs.square(xPos, yPos, switchWidth, 1, activeColor) - ecs.square(xPos + switchWidth - 2, yPos, 2, 1, passiveColor) - --ecs.colorTextWithBack(xPos + 4, yPos, passiveColor, activeColor, "ON") - else - ecs.square(xPos, yPos, switchWidth, 1, passiveColor - 0x444444) - ecs.square(xPos, yPos, 2, 1, passiveColor) - --ecs.colorTextWithBack(xPos + 4, yPos, passiveColor, passiveColor - 0x444444, "OFF") - end - newObj("Switches", number, xPos, yPos, xPos + switchWidth - 1, yPos) - - elseif objectType == "color" then - local xPos, yPos = x + 1, objects[number].y - local blendedColor = require("color").blend(objects[number][3], 0xFFFFFF, 0.705882) - local w = width - 2 - - ecs.colorTextWithBack(xPos, yPos + 2, blendedColor, background, string.rep("▀", w)) - ecs.colorText(xPos, yPos, objects[number][3], string.rep("▄", w)) - ecs.square(xPos, yPos + 1, w, 1, objects[number][3]) - - ecs.colorText(xPos + 1, yPos + 1, 0xffffff - objects[number][3], objects[number][2]) - newObj("Colors", number, xPos, yPos, x + width - 2, yPos + 2) - end - end - - --Отображение всех объектов - local function displayAllObjects() - for i = 1, countOfObjects do - displayObject(i) - end - end - - --Подготовить массив возвращаемый - local function getReturn() - local massiv = {} - - for i = 1, countOfObjects do - local type = string.lower(objects[i][1]) - - if type == "button" then - table.insert(massiv, pressedButton) - elseif type == "input" then - table.insert(massiv, objects[i][4]) - elseif type == "select" then - table.insert(massiv, objects[i][objects[i].selectedData + 3]) - elseif type == "selector" then - table.insert(massiv, objects[i].selectedElement) - elseif type == "slider" then - table.insert(massiv, objects[i][6]) - elseif type == "switch" then - table.insert(massiv, objects[i][6]) - elseif type == "color" then - table.insert(massiv, objects[i][3]) - else - table.insert(massiv, nil) - end - end - - return massiv - end - - local function redrawBeforeClose() - if closeWindowAfter then - ecs.drawOldPixels(oldPixels) - component.gpu.setBackground(oldBackground) - component.gpu.setForeground(oldForeground) - end - end - - --Рисуем окно - ecs.square(x, y, width, height, background) - displayAllObjects() - - while true do - local e = {event.pull()} - if e[1] == "touch" or e[1] == "drag" then - - --Анализируем клик на кнопки - if obj["MultiButtons"] then - for key in pairs(obj["MultiButtons"]) do - for i = 1, #obj["MultiButtons"][key] do - if ecs.clickedAtArea(e[3], e[4], obj["MultiButtons"][key][i][1], obj["MultiButtons"][key][i][2], obj["MultiButtons"][key][i][3], obj["MultiButtons"][key][i][4]) then - ecs.drawButton(obj["MultiButtons"][key][i][1], obj["MultiButtons"][key][i][2], obj["MultiButtons"][key][i][5], 3, objects[key][i + 1][3], objects[key][i + 1][2], objects[key][i + 1][1]) - os.sleep(0.3) - pressedButton = objects[key][i + 1][3] - redrawBeforeClose() - return getReturn() - end - end - end - end - - --А теперь клик на инпуты! - if obj["Inputs"] then - for key in pairs(obj["Inputs"]) do - if ecs.clickedAtArea(e[3], e[4], obj["Inputs"][key][1], obj["Inputs"][key][2], obj["Inputs"][key][3], obj["Inputs"][key][4]) then - displayObject(key, true) - displayObject(key) - break - end - end - end - - --А теперь галочковыбор! - if obj["Selects"] then - for key in pairs(obj["Selects"]) do - for i in pairs(obj["Selects"][key]) do - if ecs.clickedAtArea(e[3], e[4], obj["Selects"][key][i][1], obj["Selects"][key][i][2], obj["Selects"][key][i][3], obj["Selects"][key][i][4]) then - objects[key].selectedData = i - displayObject(key) - break - end - end - end - end - - --Хм, а вот и селектор подъехал! - if obj["Selectors"] then - for key in pairs(obj["Selectors"]) do - if ecs.clickedAtArea(e[3], e[4], obj["Selectors"][key][1], obj["Selectors"][key][2], obj["Selectors"][key][3], obj["Selectors"][key][4]) then - displayObject(key, true) - displayObject(key) - break - end - end - end - - --Слайдеры, епта! "Потный матан", все делы - if obj["Sliders"] then - for key in pairs(obj["Sliders"]) do - if ecs.clickedAtArea(e[3], e[4], obj["Sliders"][key][1], obj["Sliders"][key][2], obj["Sliders"][key][3], obj["Sliders"][key][4]) then - local xOfSlider, dolya = obj["Sliders"][key][1], obj["Sliders"][key][5] - local currentPixels = e[3] - xOfSlider - local currentValue = math.floor(currentPixels / dolya) - --Костыль - if e[3] == obj["Sliders"][key][3] then currentValue = objects[key][5] end - objects[key][6] = currentValue or objects[key][6] - displayObject(key) - break - end - end - end - - if obj["Switches"] then - for key in pairs(obj["Switches"]) do - if ecs.clickedAtArea(e[3], e[4], obj["Switches"][key][1], obj["Switches"][key][2], obj["Switches"][key][3], obj["Switches"][key][4]) then - objects[key][6] = not objects[key][6] - displayObject(key) - break - end - end - end - - if obj["Colors"] then - for key in pairs(obj["Colors"]) do - if ecs.clickedAtArea(e[3], e[4], obj["Colors"][key][1], obj["Colors"][key][2], obj["Colors"][key][3], obj["Colors"][key][4]) then - local oldColor = objects[key][3] - objects[key][3] = 0xffffff - objects[key][3] - displayObject(key) - os.sleep(0.3) - objects[key][3] = oldColor - displayObject(key) - - local paletteWidth, paletteHeight = 75, 27 - local screenWidth, screenHeight = component.gpu.getResolution() - local paletteX, paletteY = math.floor(screenWidth / 2 - paletteWidth / 2), math.floor(screenHeight / 2 - paletteHeight / 2) - local oldPixels = ecs.rememberOldPixels(paletteX, paletteY, paletteX + paletteWidth - 1, paletteY + paletteHeight - 1) - local color = require("GUI").palette(paletteX, paletteY, objects[key][3]):show() - ecs.drawOldPixels(oldPixels) - objects[key][3] = color or oldColor - - displayObject(key) - break - end - end - end - - elseif e[1] == "scroll" then - if obj["TextFields"] then - for key in pairs(obj["TextFields"]) do - if ecs.clickedAtArea(e[3], e[4], obj["TextFields"][key][1], obj["TextFields"][key][2], obj["TextFields"][key][3], obj["TextFields"][key][4]) then - if e[5] == 1 then - if objects[key].displayFrom > 1 then objects[key].displayFrom = objects[key].displayFrom - 1; displayObject(key) end - else - if objects[key].displayFrom < #objects[key].strings then objects[key].displayFrom = objects[key].displayFrom + 1; displayObject(key) end - end - end - end - end - elseif e[1] == "key_down" then - if e[4] == 28 then - redrawBeforeClose() - return getReturn() - end - end - end -end - ---Демонстрационное окно, показывающее всю мощь universalWindow -function ecs.demoWindow() - --Очищаем экран перед юзанием окна и ставим курсор на 1, 1 - ecs.prepareToExit() - --Рисуем окно и получаем данные после взаимодействия с ним - local data = ecs.universalWindow("auto", "auto", 36, 0xeeeeee, true, - {"EmptyLine"}, - {"CenterText", 0x880000, "Здорово, ебана!"}, - {"EmptyLine"}, - {"Input", 0x262626, 0x880000, "Сюда вводить можно"}, - {"Selector", 0x262626, 0x880000, "Выбор формата", "PNG", "JPG", "GIF", "PSD"}, - {"EmptyLine"}, - {"WrappedText", 0x262626, "Тест автоматического переноса букв в зависимости от ширины данного окна. Пока что тупо режет на куски, не особо красиво."}, - {"EmptyLine"}, - {"Select", 0x262626, 0x880000, "Я пидор", "Я не пидор"}, - {"Slider", 0x262626, 0x880000, 1, 100, 50, "Убито ", " младенцев"}, - {"EmptyLine"}, - {"Separator", 0xaaaaaa}, - {"Switch", 0xF2B233, 0xffffff, 0x262626, "✈ Авиарежим", false}, - {"EmptyLine"}, - {"Switch", 0x3366CC, 0xffffff, 0x262626, "☾ Не беспокоить", true}, - {"Separator", 0xaaaaaa}, - {"EmptyLine"}, - {"TextField", 5, 0xffffff, 0x262626, 0xcccccc, 0x3366CC, "Тест текстового информационного поля. По сути это тот же самый WrappedText, разве что эта хрень ограничена по высоте, и ее можно скроллить. Ну же, поскролль меня! Скролль меня полностью! Моя жадная пизда жаждет твой хуй!"}, - {"Color", "Цвет фона", 0xFF0000}, - {"EmptyLine"}, - {"Button", {0x57A64E, 0xffffff, "Да"}, {0xF2B233, 0xffffff, "Нет"}, {0xCC4C4C, 0xffffff, "Отмена"}} - ) - --Еще разок - ecs.prepareToExit() - --Выводим данные - print(" ") - print("Вывод данных из окна:") - for i = 1, #data do print("["..i.."] = "..tostring(data[i])) end - print(" ") -end - --- ecs.demoWindow() - ---[[ -Функция universalWindow(x, y, width, background, closeWindowAfter, ...) - - Это универсальная модульная функция для максимально удобного и быстрого отображения - необходимой вам информации. С ее помощью вводить данные с клавиатуры, осуществлять выбор - из предложенных вариантов, рисовать красивые кнопки, отрисовывать обычный текст, - отрисовывать текстовые поля с возможностью прокрутки, рисовать разделители и прочее. - Любой объект выделяется с помощью клика мыши, после чего функция приступает к работе - с этим объектом. - -Аргументы функции: - - x и y: это числа, обозначающие стартовые координаты левого верхнего угла данного окна. - Вместо цифр вы также можете написать "auto" - и программа автоматически разместит окно - по центру экрана по выбранной координате. Или по обеим координатам, если вам угодно. - - width: это ширина окна, которую вы можете задать по собственному желанию. Если некторые - объекты требуют расширения окна, то окно будет автоматически расширено до нужной ширины. - Да, вот такая вот тавтология ;) - - background: базовый цвет окна (цвет фона, кому как понятнее). - - closeWindowAfter: eсли true, то окно по завершению функции будет выгружено, а на его месте - отрисуются пиксели, которые имелись на экране до выполнения функции. Удобно, если не хочешь - париться с перерисовкой интерфейса. - - ... : многоточием тут является перечень объектов, указанных через запятую. Каждый объект - является массивом и имеет собственный формат. Ниже перечислены все возможные типы объектов. - - {"Button", {Цвет кнопки1, Цвет текста на кнопке1, Сам текст1}, {Цвет кнопки2, Цвет текста на кнопке2, Сам текст2}, ...} - - Это объект для рисования кнопок. Каждая кнопка - это массив, состоящий из трех элементов: - цвета кнопки, цвета текста на кнопке и самого текста. Кнопок может быть неограниченное количество, - однако чем их больше, тем большее требуется разрешение экрана по ширине. - - Интерактивный объект. - - {"Input", Цвет рамки и текста, Цвет при выделении, Стартовый текст [, Маскировать символом]} - - Объект для рисования полей ввода текстовой информации. Удобно для открытия или сохранения файлов, - Опциональный аргумент "Маскировать символом" полезен, если вы делаете поле для ввода пароля. - Никто не увидит ваш текст. В качестве данного аргумента передается символ, например "*". - - Интерактивный объект. - - {"Selector", Цвет рамки, Цвет при выделении, Выбор 1, Выбор 2, Выбор 3 ...} - - Внешне схож с объектом "Input", однако в этом случае вы будете выбирать один из предложенных - вариантов из выпадающего списка. По умолчанию выбран первый вариант. - - Интерактивный объект. - - {"Select", Цвет рамки, Цвет галочки, Выбор 1, Выбор 2, Выбор 3 ...} - - Объект выбора. Отличается от "Selector" тем, что здесь вы выбираете один из вариантов, отмечая - его галочкой. По умолчанию выбран первый вариант. - - Интерактивный объект. - - {"Slider", Цвет линии слайдера, Цвет пимпочки слайдера, Значения слайдера ОТ, Значения слайдера ДО, Текущее значение [, Текст-подсказка ДО] [, Текст-подсказка ПОСЛЕ]} - - Ползунок, позволяющий задавать определенное количество чего-либо в указанном интервале. Имеются два - опциональных аргумента, позволяющих четко понимать, с чем именно мы имеем дело. - - К примеру, если аргумент "Текст-подсказка ДО" будет равен "Съедено ", а аргумент "Текст-подсказка ПОСЛЕ" - будет равен " яблок", а значение слайдера будет равно 50, то на экране будет написано "Съедено 50 яблок". - - Интерактивный объект. - - {"Switch", Активный цвет, Пассивный цвет, Цвет текста, Текст, Состояние} - - Переключатель, принимающий два состояния: true или false. Текст - это всего лишь информация, некое - название данного переключателя. - - Интерактивный объект. - - {"CenterText", Цвет текста, Сам текст} - - Отображение текста указанного цвета по центру окна. Чисто для информативных целей. - - {"WrappedText", Цвет текста, Текст} - - Отображение большого количества текста с автоматическим переносом. Прото режет слова на кусочки, - перенос символический. Чисто для информативных целей. - - {"TextField", Высота, Цвет фона, Цвет текста, Цвет скроллбара, Цвет пимпочки скроллбара, Сам текст} - - Текстовое поле с возможностью прокрутки. Отличается от "WrappedText" - фиксированной высотой. Чисто для информативных целей. - - {"Separator", Цвет разделителя} - - Линия-разделитель, помогающая лучше отделять объекты друг от друга. Декоративный объект. - - {"EmptyLine"} - - Пустое пространство, помогающая лучше отделять объекты друг от друга. Декоративный объект. - - Каждый из объектов рисуется по порядку сверху вниз. Каждый объект автоматически - увеличивает высоту окна до необходимого значения. Если объектов будет указано слишком много - - т.е. если окно вылезет за пределы экрана, то программа завершится с ошибкой. - - Что возвращает функция: - - Возвратом является массив, пронумерованный от 1 до <количества объектов>. - К примеру, 1 индекс данного массива соответствует 1 указанному объекту. - Каждый индекс данного массива несет в себе какие-то данные, которые вы - внесли в объект во время работы функции. - Например, если в 1-ый объект типа "Input" вы ввели фразу "Hello world", - то первый индекс в возвращенном массиве будет равен "Hello world". - Конкретнее это будет вот так: massiv[1] = "Hello world". - - Если взаимодействие с объектом невозможно - например, как в случае - с EmptyLine, CenterText, TextField или Separator, то в возвращенном - массиве этот объект указываться не будет. - - Готовые примеры использования функции указаны ниже и закомментированы. - Выбирайте нужный и раскомментируйте. -]] - ---Функция-демонстратор, показывающая все возможные объекты в одном окне. Код окна находится выше. ---ecs.demoWindow() - ---Функция, предлагающая сохранить файл в нужном месте в нужном формате. ---ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Сохранить как"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Путь"}, {"Selector", 0x262626, 0x880000, "PNG", "JPG", "PSD"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK!"}}) - ----------------------------------------------------------------------------------------------------- - -return ecs - - diff --git a/lib/FormatModules/OCIF.lua b/lib/FormatModules/OCIF.lua deleted file mode 100755 index aca66185..00000000 --- a/lib/FormatModules/OCIF.lua +++ /dev/null @@ -1,201 +0,0 @@ - -local args = {...} -local image = args[1] - ----------------------------------------- Libraries ---------------------------------------- - -local bit32 = require("bit32") -require("advancedLua") -local unicode = require("unicode") -local fs = require("filesystem") -local color = require("color") - ------------------------------------------------------------------------------------------------------------- - -local module = {} -local OCIFSignature = "OCIF" -local encodingMethods = { - load = {}, - save = {} -} - ------------------------------------------------------------------------------------------------------------- - -local function writeByteArrayToFile(file, byteArray) - for i = 1, #byteArray do - file:write(string.char(byteArray[i])) - end -end - -local function readNumberFromFile(file, countOfBytes) - local byteArray = {} - for i = 1, countOfBytes do - table.insert(byteArray, string.byte(file:read(1))) - end - - return bit32.byteArrayToNumber(byteArray) -end - ----------------------------------------- Uncompressed OCIF1 encoding ---------------------------------------- - -encodingMethods.save[5] = function(file, picture) - for i = 3, #picture, 4 do - file:write(string.char(color.to8Bit(picture[i]))) - file:write(string.char(color.to8Bit(picture[i + 1]))) - file:write(string.char(math.floor(picture[i + 2] * 255))) - writeByteArrayToFile(file, {string.byte(picture[i + 3], 1, 6)}) - end -end - -encodingMethods.load[5] = function(file, picture) - table.insert(picture, readNumberFromFile(file, 2)) - table.insert(picture, readNumberFromFile(file, 2)) - - for i = 1, image.getWidth(picture) * image.getHeight(picture) do - table.insert(picture, color.to24Bit(string.byte(file:read(1)))) - table.insert(picture, color.to24Bit(string.byte(file:read(1)))) - table.insert(picture, string.byte(file:read(1)) / 255) - table.insert(picture, fs.readUnicodeChar(file)) - end -end - ----------------------------------------- Grouped and compressed OCIF6 encoding ---------------------------------------- - -encodingMethods.save[6] = function(file, picture) - -- Grouping picture by it's alphas, symbols and colors - local groupedPicture = image.group(picture, true) - -- Writing 1 byte for alphas array size - file:write(string.char(table.size(groupedPicture))) - - for alpha in pairs(groupedPicture) do - -- Writing 1 byte for current alpha value - file:write(string.char(math.floor(alpha * 255))) - -- Writing 2 bytes for symbols array size - writeByteArrayToFile(file, bit32.numberToFixedSizeByteArray(table.size(groupedPicture[alpha]), 2)) - - for symbol in pairs(groupedPicture[alpha]) do - -- Writing N bytes for current unicode symbol value - writeByteArrayToFile(file, { string.byte(symbol, 1, 6) }) - -- Writing 1 byte for backgrounds array size - file:write(string.char(table.size(groupedPicture[alpha][symbol]))) - - for background in pairs(groupedPicture[alpha][symbol]) do - -- Writing 1 byte for background color value (compressed by color) - file:write(string.char(background)) - -- Writing 1 byte for foregrounds array size - file:write(string.char(table.size(groupedPicture[alpha][symbol][background]))) - - for foreground in pairs(groupedPicture[alpha][symbol][background]) do - -- Writing 1 byte for foreground color value (compressed by color) - file:write(string.char(foreground)) - -- Writing 1 byte for y array size - file:write(string.char(table.size(groupedPicture[alpha][symbol][background][foreground]))) - - for y in pairs(groupedPicture[alpha][symbol][background][foreground]) do - -- Writing 1 byte for current y value - file:write(string.char(y)) - -- Writing 1 byte for x array size - file:write(string.char(#groupedPicture[alpha][symbol][background][foreground][y])) - - for x = 1, #groupedPicture[alpha][symbol][background][foreground][y] do - file:write(string.char(groupedPicture[alpha][symbol][background][foreground][y][x])) - end - end - end - end - end - end -end - -encodingMethods.load[6] = function(file, picture) - table.insert(picture, string.byte(file:read(1))) - table.insert(picture, string.byte(file:read(1))) - - local currentAlpha, currentSymbol, currentBackground, currentForeground, currentY, currentX - local alphaSize, symbolSize, backgroundSize, foregroundSize, ySize, xSize - - alphaSize = string.byte(file:read(1)) - - for alpha = 1, alphaSize do - currentAlpha = string.byte(file:read(1)) / 255 - symbolSize = readNumberFromFile(file, 2) - - for symbol = 1, symbolSize do - currentSymbol = fs.readUnicodeChar(file) - backgroundSize = string.byte(file:read(1)) - - for background = 1, backgroundSize do - currentBackground = color.to24Bit(string.byte(file:read(1))) - foregroundSize = string.byte(file:read(1)) - - for foreground = 1, foregroundSize do - currentForeground = color.to24Bit(string.byte(file:read(1))) - ySize = string.byte(file:read(1)) - - for y = 1, ySize do - currentY = string.byte(file:read(1)) - xSize = string.byte(file:read(1)) - - for x = 1, xSize do - currentX = string.byte(file:read(1)) - image.set(picture, currentX, currentY, currentBackground, currentForeground, currentAlpha, currentSymbol) - end - end - end - end - end - end -end - ----------------------------------------- Public load&save methods of module ---------------------------------------- - -function module.load(path) - local file, reason = io.open(path, "rb") - if file then - local readedSignature = file:read(#OCIFSignature) - if readedSignature == OCIFSignature then - local encodingMethod = string.byte(file:read(1)) - if encodingMethods.load[encodingMethod] then - local picture = {} - encodingMethods.load[encodingMethod](file, picture) - - file:close() - return picture - else - file:close() - return false, "Failed to load OCIF image: encoding method \"" .. tostring(encodingMethod) .. "\" is not supported" - end - else - file:close() - return false, "Failed to load OCIF image: binary signature \"" .. tostring(readedSignature) .. "\" is not valid" - end - else - return false, "Failed to open file \"" .. tostring(path) .. "\" for reading: " .. tostring(reason) - end -end - -function module.save(path, picture, encodingMethod) - encodingMethod = encodingMethod or 6 - - local file, reason = io.open(path, "wb") - if file then - if encodingMethods.save[encodingMethod] then - file:write(OCIFSignature, string.char(encodingMethod), string.char(picture[1]), string.char(picture[2])) - encodingMethods.save[encodingMethod](file, picture) - - file:close() - return true - else - file:close() - return false, "Failed to save file as OCIF image: encoding method \"" .. tostring(encodingMethod) .. "\" is not supported" - end - else - return false, "Failed to open file for writing: " .. tostring(reason) - end -end - ------------------------------------------------------------------------------------------------------------- - -return module - - diff --git a/lib/FormatModules/RAW.lua b/lib/FormatModules/RAW.lua deleted file mode 100755 index a0b8668a..00000000 --- a/lib/FormatModules/RAW.lua +++ /dev/null @@ -1,70 +0,0 @@ - -local args = {...} -local image = args[1] - -local unicode = require("unicode") -local module = {} - --------------------------------------------------------------------------------------------------------------- - -function module.load(path) - local file, reason = io.open(path, "r") - if file then - local picture, pictureWidth, lineCounter = {0, 0}, nil, 0 - for line in file:lines() do - local lineLength = unicode.len(line) - if not pictureWidth then - pictureWidth = (lineLength + 1) / 19 - picture[1] = pictureWidth - end - - for x = 1, lineLength, 19 do - table.insert(picture, tonumber("0x" .. unicode.sub(line, x, x + 5))) - table.insert(picture, tonumber("0x" .. unicode.sub(line, x + 7, x + 12))) - table.insert(picture, tonumber("0x" .. unicode.sub(line, x + 14, x + 15))) - table.insert(picture, unicode.sub(line, x + 17, x + 17)) - end - - lineCounter = lineCounter + 1 - end - - picture[2] = lineCounter - file:close() - return picture - else - error("Failed to open file \"" .. tostring(path) .. "\" for reading: " .. tostring(reason)) - end -end - -function module.save(path, picture, encodingMethod) - local file, reason = io.open(path, "w") - if file then - local x = 1 - for i = 3, #picture, 4 do - file:write( - string.format("%06X", picture[i]), " ", - string.format("%06X", picture[i + 1]), " ", - string.format("%02X", picture[i + 2]), " ", - picture[i + 3] - ) - - x = x + 1 - if x > picture[1] then - x = 1 - file:write("\n") - else - file:write(" ") - end - end - - file:close() - else - error("Failed to open file for writing: " .. tostring(reason)) - end -end - --------------------------------------------------------------------------------------------------------------- - -return module - - diff --git a/lib/MineOSCore.lua b/lib/MineOSCore.lua deleted file mode 100755 index baf61b2f..00000000 --- a/lib/MineOSCore.lua +++ /dev/null @@ -1,311 +0,0 @@ - -require("advancedLua") -local component = require("component") -local buffer = require("doubleBuffering") -local fs = require("filesystem") -local unicode = require("unicode") -local image = require("image") -local color = require("color") -local MineOSPaths = require("MineOSPaths") - ----------------------------------------------------------------------------------------------------------------- - -local MineOSCore = {} -MineOSCore.localization = {} - ----------------------------------------------------------------------------------------------------------------- - -function MineOSCore.getCurrentScriptDirectory() - return fs.path(getCurrentScript()) -end - -function MineOSCore.getTemporaryPath() - local temporaryPath - repeat - temporaryPath = MineOSPaths.temporary .. string.format("%08X", math.random(0xFFFFFFFF)) - until not fs.exists(temporaryPath) - - return temporaryPath -end - -function MineOSCore.getLocalization(pathToLocalizationFolder) - local required, english = pathToLocalizationFolder .. MineOSCore.properties.language .. ".lang", pathToLocalizationFolder .. "English.lang" - if fs.exists(required) then - return table.fromFile(required) - elseif fs.exists(english) then - return table.fromFile(english) - else - return table.fromFile(pathToLocalizationFolder .. fs.list(pathToLocalizationFolder)()) - end -end - -function MineOSCore.getCurrentScriptLocalization() - return MineOSCore.getLocalization(MineOSCore.getCurrentScriptDirectory() .. "Localizations/") -end - -function MineOSCore.getCurrentApplicationResourcesDirectory() - return MineOSCore.getCurrentScriptDirectory() -end - -function MineOSCore.getCurrentApplicationLocalization() - return MineOSCore.getLocalization(MineOSCore.getCurrentApplicationResourcesDirectory() .. "Localizations/") -end - ------------------------------------------------------------------------------------------------------------------------------------ - -function MineOSCore.createShortcut(where, forWhat) - fs.makeDirectory(fs.path(where)) - local file = io.open(where, "w") - file:write(forWhat) - file:close() -end - -function MineOSCore.readShortcut(path) - local file, reason = io.open(path, "r") - if file then - local data = file:read("*a") - file:close() - return data - else - error("Failed to read shortcut \"" .. tostring(path) .. "\": " .. tostring(reason)) - end -end - ------------------------------------------------------------------------------------------------------------------------------------ - -function MineOSCore.saveProperties() - table.toFile(MineOSPaths.properties, MineOSCore.properties, true) -end - -function MineOSCore.loadPropeties() - local saveLater = false - - if fs.exists(MineOSPaths.properties) then - MineOSCore.properties = table.fromFile(MineOSPaths.properties) - else - MineOSCore.properties = {} - saveLater = true - end - - local defaultValues = { - tasks = {}, - timeUseRealTimestamp = true, - dateFormat = "%d %b %Y %H:%M:%S", - packageUnloading = false, - transparencyEnabled = true, - showApplicationIcons = true, - iconHorizontalSpaceBetween = 1, - iconVerticalSpaceBetween = 1, - iconWidth = 12, - iconHeight = 6, - showExtension = false, - wallpaper = MineOSPaths.pictures .. "Space.pic", - wallpaperMode = 2, - wallpaperBrightness = 0.9, - screensaver = "Matrix", - screensaverDelay = 20, - timezone = 3, - dockColor = 0xFFFFFF, - menuColor = 0xE1E1E1, - backgroundColor = 0x1E1E1E, - dockShortcuts = { - MineOSPaths.applications .. "App Market.app/", - MineOSPaths.applications .. "MineCode IDE.app/", - MineOSPaths.applications .. "Finder.app/", - MineOSPaths.applications .. "Picture Edit.app/", - MineOSPaths.applications .. "Settings.app/", - }, - network = { - users = {}, - enabled = true, - signalStrength = 512, - }, - FTPConnections = {}, - } - - MineOSCore.associateExtension(".pic", MineOSPaths.imageEditor, MineOSPaths.icons .. "/Image.pic", MineOSPaths.extensionAssociations .. "Pic/ContextMenu.lua") - MineOSCore.associateExtension(".txt", MineOSPaths.editor, MineOSPaths.icons .. "/Text.pic") - MineOSCore.associateExtension(".cfg", MineOSPaths.editor, MineOSPaths.icons .. "/Config.pic") - MineOSCore.associateExtension(".arc", MineOSPaths.extensionAssociations .. "Arc/Launcher.lua", MineOSPaths.icons .. "/Archive.pic") - - MineOSCore.associateExtension(".3dm", MineOSPaths.applications .. "/3D Print.app/Main.lua", MineOSPaths.icons .. "/3DModel.pic", MineOSPaths.extensionAssociations .. "3dm/ContextMenu.lua") - MineOSCore.associateExtension("script", MineOSPaths.extensionAssociations .. "Lua/Launcher.lua", MineOSPaths.icons .. "/Script.pic", MineOSPaths.extensionAssociations .. "Lua/ContextMenu.lua") - MineOSCore.associateExtension(".lua", MineOSPaths.extensionAssociations .. "Lua/Launcher.lua", MineOSPaths.icons .. "/Lua.pic", MineOSPaths.extensionAssociations .. "Lua/ContextMenu.lua") - - for key, value in pairs(defaultValues) do - if MineOSCore.properties[key] == nil then - MineOSCore.properties[key] = value - saveLater = true - end - end - - if saveLater then - MineOSCore.saveProperties() - end -end - ------------------------------------------------------------------------------------------------------------------------------------ - -function MineOSCore.associateExtensionLauncher(extension, pathToLauncher) - MineOSCore.properties.extensionAssociations = MineOSCore.properties.extensionAssociations or {} - MineOSCore.properties.extensionAssociations[extension] = MineOSCore.properties.extensionAssociations[extension] or {} - MineOSCore.properties.extensionAssociations[extension].launcher = pathToLauncher -end - -function MineOSCore.associateExtensionIcon(extension, pathToIcon) - MineOSCore.properties.extensionAssociations[extension] = MineOSCore.properties.extensionAssociations[extension] or {} - MineOSCore.properties.extensionAssociations[extension].icon = pathToIcon -end - -function MineOSCore.associateExtensionContextMenu(extension, pathToContextMenu) - MineOSCore.properties.extensionAssociations[extension] = MineOSCore.properties.extensionAssociations[extension] or {} - MineOSCore.properties.extensionAssociations[extension].contextMenu = pathToContextMenu -end - -function MineOSCore.associateExtension(extension, pathToLauncher, pathToIcon, pathToContextMenu) - MineOSCore.associateExtensionLauncher(extension, pathToLauncher) - MineOSCore.associateExtensionIcon(extension, pathToIcon) - MineOSCore.associateExtensionContextMenu(extension, pathToContextMenu) -end - -function MineOSCore.associationsExtensionAutomatically() - local path, extension = MineOSPaths.extensionAssociations - for file in fs.list(path) do - if fs.isDirectory(path .. file) then - extension = "." .. unicode.sub(file, 1, -2) - - if fs.exists(path .. file .. "ContextMenu.lua") then - MineOSCore.associateExtensionContextMenu(extension, path .. file .. "Context menu.lua") - end - - if fs.exists(path .. file .. "Launcher.lua") then - MineOSCore.associateExtensionLauncher(extension, path .. file .. "Launcher.lua") - end - end - end -end - ------------------------------------------------------------------------------------------------------------------------------------ - ---Функция парсинга Lua-сообщения об ошибке. Конвертирует из строки в массив. -function MineOSCore.parseErrorMessage(error, indentationWidth) - local parsedError = {} - - --Замена /r/n и табсов - error = string.gsub(error, "\r\n", "\n") - error = string.gsub(error, " ", string.rep(" ", indentationWidth or 4)) - - --Удаление энтеров - local searchFrom, starting, ending = 1 - for i = 1, unicode.len(error) do - starting, ending = string.find(error, "\n", searchFrom) - if starting then - table.insert(parsedError, unicode.sub(error, searchFrom, starting - 1)) - searchFrom = ending + 1 - else - break - end - end - - --На всякий случай, если сообщение об ошибке без энтеров вообще, т.е. однострочное - if #parsedError == 0 then table.insert(parsedError, error) end - - return parsedError -end - -function MineOSCore.call(method, ...) - local args = {...} - local function launchMethod() - method(table.unpack(args)) - end - - local function tracebackMethod(xpcallTraceback) - local traceback, info, firstMatch = tostring(xpcallTraceback) .. "\n" .. debug.traceback() - for runLevel = 0, math.huge do - info = debug.getinfo(runLevel) - if info then - if (info.what == "main" or info.what == "Lua") and info.source ~= "=machine" then - if firstMatch then - return { - path = info.source:sub(2, -1), - line = info.currentline, - traceback = traceback - } - else - firstMatch = true - end - end - else - error("Failed to get debug info for runlevel " .. runLevel) - end - end - end - - local xpcallSuccess, xpcallReason = xpcall(launchMethod, tracebackMethod) - if type(xpcallReason) == "string" or type(xpcallReason) == "nil" then xpcallReason = {path = "/lib/MineOSCore.lua", line = 1, traceback = "MineOSCore fatal error: " .. tostring(xpcallReason)} end - if not xpcallSuccess and not xpcallReason.traceback:match("^table") and not xpcallReason.traceback:match("interrupted") then - return false, xpcallReason.path, xpcallReason.line, xpcallReason.traceback - end - - return true -end - -function MineOSCore.safeLaunch(path, ...) - path = path:gsub("/+", "/") - - local oldResolutionWidth, oldResolutionHeight = buffer.getResolution() - local finalSuccess, finalPath, finalLine, finalTraceback = true - - if fs.exists(path) then - local loadSuccess, loadReason = loadfile("/" .. path) - if loadSuccess then - local success, path, line, traceback = MineOSCore.call(loadSuccess, ...) - if not success then - finalSuccess, finalPath, finalLine, finalTraceback = false, path, line, traceback - end - else - local match = string.match(loadReason, ":(%d+)%:") - finalSuccess, finalPath, finalLine, finalTraceback = false, path, tonumber(match) or 1, loadReason - end - else - require("GUI").alert("Failed to safely launch file that doesn't exists: \"" .. path .. "\"") - end - - component.screen.setPrecise(false) - buffer.setResolution(oldResolutionWidth, oldResolutionHeight) - - return finalSuccess, finalPath, finalLine, finalTraceback -end - -function MineOSCore.setPackageUnloading(value) - local metatable = getmetatable(package.loaded) - - if value then - if metatable then - metatable.__mode = "v" - else - setmetatable(package.loaded, {__mode = "v"}) - end - else - if metatable then - metatable.__mode = nil - - for key in pairs(metatable) do - return - end - - setmetatable(package.loaded, nil) - end - end -end - ------------------------------------------------------------------------------------------------------------------------------------ - -fs.remove(MineOSPaths.temporary) -fs.makeDirectory(MineOSPaths.temporary) -MineOSCore.loadPropeties() -MineOSCore.setPackageUnloading(MineOSCore.properties.packageUnloading) - ------------------------------------------------------------------------------------------------------------------------------------ - -return MineOSCore \ No newline at end of file diff --git a/lib/MineOSInterface.lua b/lib/MineOSInterface.lua deleted file mode 100755 index 245af747..00000000 --- a/lib/MineOSInterface.lua +++ /dev/null @@ -1,1513 +0,0 @@ - -local component = require("component") -local computer = require("computer") -local keyboard = require("keyboard") -local event = require("event") -local term = require("term") -local MineOSCore = require("MineOSCore") -local MineOSPaths = require("MineOSPaths") -local image = require("image") -local GUI = require("GUI") -local fs = require("filesystem") -local unicode = require("unicode") -local buffer = require("doubleBuffering") -local MineOSInterface = {} - ------------------------------------------------------------------------------------------------------------------------------------ - -MineOSInterface.iconsCache = {} -MineOSInterface.iconClickDelay = 0.2 -MineOSInterface.iconConfigFileName = ".icons" -MineOSInterface.iconImageWidth = 8 -MineOSInterface.iconImageHeight = 4 - ------------------------------------------------------------------------------------------------------------------------------------ - -local function calculateIconSizes() - MineOSInterface.iconHalfWidth = math.floor(MineOSCore.properties.iconWidth / 2) - MineOSInterface.iconTextHeight = MineOSCore.properties.iconHeight - MineOSInterface.iconImageHeight - 1 - MineOSInterface.iconImageHorizontalOffset = math.floor(MineOSInterface.iconHalfWidth - MineOSInterface.iconImageWidth / 2) -end - -function MineOSInterface.setIconProperties(width, height, horizontalSpaceBetween, verticalSpaceBetween) - MineOSCore.properties.iconWidth, MineOSCore.properties.iconHeight, MineOSCore.properties.iconHorizontalSpaceBetween, MineOSCore.properties.iconVerticalSpaceBetween = width, height, horizontalSpaceBetween, verticalSpaceBetween - MineOSCore.saveProperties() - calculateIconSizes() - - MineOSInterface.application.iconField:deleteIconConfig() - MineOSInterface.application.dockContainer.sort() - - computer.pushSignal("MineOSCore", "updateFileList") -end - -calculateIconSizes() - ------------------------------------------------------------------------------------------------------------------------------------ - -function MineOSInterface.clearTerminal() - local gpu = component.gpu - gpu.setBackground(0x1D1D1D) - gpu.setForeground(0xFFFFFF) - local width, height = gpu.getResolution() - gpu.fill(1, 1, width, height, " ") - term.setCursor(1, 1) -end - -function MineOSInterface.waitForPressingAnyKey() - print(" ") - print(MineOSCore.localization.pressAnyKeyToContinue) - while true do - local eventType = event.pull() - if eventType == "key_down" or eventType == "touch" then - break - end - end -end - -function MineOSInterface.launchScript(path) - MineOSInterface.clearTerminal() - if MineOSInterface.safeLaunch(path) then - MineOSInterface.waitForPressingAnyKey() - end -end - ------------------------------------------------------------------------------------------------------------------------------------ - -function MineOSInterface.cacheIconSource(name, path) - if not MineOSInterface.iconsCache[name] then - MineOSInterface.iconsCache[name] = image.load(path) - end - - return MineOSInterface.iconsCache[name] -end - -local function iconDraw(icon) - local selectionTransparency = MineOSCore.properties.transparencyEnabled and 0.5 - local text = MineOSCore.properties.showExtension and icon.name or icon.nameWithoutExtension - local xCenter, yText = icon.x + MineOSInterface.iconHalfWidth, icon.y + MineOSInterface.iconImageHeight + 1 - - local function iconDrawNameLine(y, line) - local lineLength = unicode.len(line) - local x = math.floor(xCenter - lineLength / 2) - - if icon.selected then - buffer.drawRectangle(x, y, lineLength, 1, icon.colors.selection, 0x0, " ", selectionTransparency) - end - buffer.drawText(x, y, icon.colors.text, line) - end - - local charIndex = 1 - for lineIndex = 1, MineOSInterface.iconTextHeight do - if lineIndex < MineOSInterface.iconTextHeight then - iconDrawNameLine(yText, unicode.sub(text, charIndex, charIndex + icon.width - 1)) - charIndex, yText = charIndex + icon.width, yText + 1 - else - iconDrawNameLine(yText, string.limit(unicode.sub(text, charIndex, -1), icon.width, "center")) - end - end - - local xImage = icon.x + MineOSInterface.iconImageHorizontalOffset - if icon.selected then - local xSelection = xImage - 1 - buffer.drawText(xSelection, icon.y - 1, icon.colors.selection, string.rep("▄", MineOSInterface.iconImageWidth + 2), selectionTransparency) - buffer.drawText(xSelection, icon.y + MineOSInterface.iconImageHeight, icon.colors.selection, string.rep("▀", MineOSInterface.iconImageWidth + 2), selectionTransparency) - buffer.drawRectangle(xSelection, icon.y, MineOSInterface.iconImageWidth + 2, MineOSInterface.iconImageHeight, icon.colors.selection, 0x0, " ", selectionTransparency) - end - - if icon.image then - if icon.cut then - if not icon.semiTransparentImage then - icon.semiTransparentImage = image.copy(icon.image) - for i = 1, #icon.semiTransparentImage[3] do - icon.semiTransparentImage[5][i] = icon.semiTransparentImage[5][i] + 0.6 - if icon.semiTransparentImage[5][i] > 1 then - icon.semiTransparentImage[5][i] = 1 - end - end - end - - buffer.drawImage(xImage, icon.y, icon.semiTransparentImage, true) - else - buffer.drawImage(xImage, icon.y, icon.image) - end - elseif icon.liveImage then - icon.liveImage(xImage, icon.y) - end - - local xShortcut = xImage + MineOSInterface.iconImageWidth - if icon.isShortcut then - buffer.set(xShortcut - 1, icon.y + MineOSInterface.iconImageHeight - 1, 0xFFFFFF, 0x0, "<") - end - - if icon.windows then - buffer.drawText(xCenter - 1, icon.y + MineOSInterface.iconImageHeight, 0x66DBFF, "╺╸") - - local windowCount = table.size(icon.windows) - if windowCount > 1 then - - windowCount = tostring(windowCount) - local windowCountLength = #windowCount - local xTip, yTip = xShortcut - windowCountLength, icon.y - - buffer.drawRectangle(xTip, yTip, windowCountLength, 1, 0xFF4940, 0xFFFFFF, " ") - buffer.drawText(xTip, yTip, 0xFFFFFF, windowCount) - buffer.drawText(xTip - 1, yTip, 0xFF4940, "⢸") - buffer.drawText(xTip + windowCountLength, yTip, 0xFF4940, "⡇") - buffer.drawText(xTip, yTip - 1, 0xFF4940, string.rep("⣀", windowCountLength)) - buffer.drawText(xTip, yTip + 1, 0xFF4940, string.rep("⠉", windowCountLength)) - end - end -end - -local function iconEventHandler(application, object, e1, e2, e3, e4, e5, ...) - if e1 == "touch" and object:isPointInside(e3, e4) then - object.lastTouchPosition = object.lastTouchPosition or {} - object.lastTouchPosition.x, object.lastTouchPosition.y = e3, e4 - object:moveToFront() - - if e5 == 0 then - object.parent.parent.onLeftClick(object, e1, e2, e3, e4, e5, ...) - else - object.parent.parent.onRightClick(object, e1, e2, e3, e4, e5, ...) - end - elseif e1 == "double_touch" and object:isPointInside(e3, e4) and e5 == 0 then - object.parent.parent.onDoubleClick(object, e1, e2, e3, e4, e5, ...) - elseif e1 == "drag" and object.parent.parent.iconConfigEnabled and object.lastTouchPosition then - -- Ебучие авторы мода, ну на кой хуй было делать drop-ивент без наличия drag? ПИДОРЫ - object.dragStarted = true - object.localX = object.localX + e3 - object.lastTouchPosition.x - object.localY = object.localY + e4 - object.lastTouchPosition.y - object.lastTouchPosition.x, object.lastTouchPosition.y = e3, e4 - - application:draw() - elseif e1 == "drop" and object.parent.parent.iconConfigEnabled and object.dragStarted then - object.dragStarted = nil - object.parent.parent.iconConfig[object.name .. (object.isDirectory and "/" or "")] = { - x = object.localX, - y = object.localY - } - object.parent.parent:saveIconConfig() - object.lastTouchPosition = nil - end -end - -local function iconAnalyseExtension(icon) - if icon.isDirectory then - if icon.extension == ".app" then - if MineOSCore.properties.showApplicationIcons then - if fs.exists(icon.path .. "Icon.pic") then - icon.image = image.load(icon.path .. "Icon.pic") - elseif fs.exists(icon.path .. "Resources/Icon.pic") then - icon.image = image.load(icon.path .. "Resources/Icon.pic") - elseif fs.exists(icon.path .. "Icon.lua") then - local result, reason = loadfile(icon.path .. "Icon.lua") - if result then - result, reason = pcall(result) - if result then - icon.liveImage = reason - else - error("Failed to load live icon image: " .. tostring(reason)) - end - else - error("Failed to load live icon image: " .. tostring(reason)) - end - else - icon.image = MineOSInterface.iconsCache.fileNotExists - end - else - icon.image = MineOSInterface.iconsCache.application - end - - icon.launch = icon.launchers.application - else - icon.image = MineOSInterface.iconsCache.folder - icon.launch = icon.launchers.directory - end - else - if icon.extension == ".lnk" then - icon.shortcutPath = MineOSCore.readShortcut(icon.path) - icon.shortcutExtension = fs.extension(icon.shortcutPath) - icon.shortcutIsDirectory = fs.isDirectory(icon.shortcutPath) - icon.isShortcut = true - - local shortcutIcon = iconAnalyseExtension({ - path = icon.shortcutPath, - extension = icon.shortcutExtension, - name = icon.name, - nameWithoutExtension = icon.nameWithoutExtension, - isDirectory = icon.shortcutIsDirectory, - iconImage = icon.iconImage, - launchers = icon.launchers - }) - - icon.image = shortcutIcon.image - icon.shortcutLaunch = shortcutIcon.launch - icon.launch = icon.launchers.shortcut - - shortcutIcon = nil - elseif not fs.exists(icon.path) then - icon.image = MineOSInterface.iconsCache.fileNotExists - icon.launch = icon.launchers.corrupted - else - if MineOSCore.properties.extensionAssociations[icon.extension] then - icon.launch = icon.launchers.extension - icon.image = MineOSInterface.cacheIconSource(icon.extension, MineOSCore.properties.extensionAssociations[icon.extension].icon) - else - icon.launch = icon.launchers.script - icon.image = MineOSInterface.iconsCache.script - end - end - end - - return icon -end - -local function iconIsPointInside(icon, x, y) - return - x >= icon.x + MineOSInterface.iconImageHorizontalOffset and - y >= icon.y and - x <= icon.x + MineOSInterface.iconImageHorizontalOffset + MineOSInterface.iconImageWidth - 1 and - y <= icon.y + MineOSInterface.iconImageHeight - 1 - or - x >= icon.x and - y >= icon.y + MineOSInterface.iconImageHeight + 1 and - x <= icon.x + MineOSCore.properties.iconWidth - 1 and - y <= icon.y + MineOSCore.properties.iconHeight - 1 -end - -function MineOSInterface.icon(x, y, path, textColor, selectionColor) - local icon = GUI.object(x, y, MineOSCore.properties.iconWidth, MineOSCore.properties.iconHeight) - - icon.colors = { - text = textColor, - selection = selectionColor - } - - icon.path = path - icon.extension = fs.extension(icon.path) or "script" - icon.name = fs.name(path) - icon.nameWithoutExtension = fs.hideExtension(icon.name) - icon.isDirectory = fs.isDirectory(icon.path) - icon.isShortcut = false - icon.selected = false - - icon.isPointInside = iconIsPointInside - icon.draw = iconDraw - icon.launchers = table.copy(MineOSInterface.iconLaunchers) - icon.analyseExtension = iconAnalyseExtension - - return icon -end - -local function iconFieldUpdate(iconField) - iconField.backgroundObject.width, iconField.backgroundObject.height = iconField.width, iconField.height - iconField.iconsContainer.width, iconField.iconsContainer.height = iconField.width, iconField.height - - iconField.iconCount.horizontal = math.floor((iconField.width - iconField.xOffset) / (MineOSCore.properties.iconWidth + MineOSCore.properties.iconHorizontalSpaceBetween)) - iconField.iconCount.vertical = math.floor((iconField.height - iconField.yOffset) / (MineOSCore.properties.iconHeight + MineOSCore.properties.iconVerticalSpaceBetween)) - iconField.iconCount.total = iconField.iconCount.horizontal * iconField.iconCount.vertical - - return iconField -end - -local function iconFieldLoadIconConfig(iconField) - if fs.exists(iconField.workpath .. MineOSInterface.iconConfigFileName) then - iconField.iconConfig = table.fromFile(iconField.workpath .. MineOSInterface.iconConfigFileName) - else - iconField.iconConfig = {} - end -end - -local function iconFieldSaveIconConfig(iconField) - table.toFile(iconField.workpath .. MineOSInterface.iconConfigFileName, iconField.iconConfig) -end - -local function iconFieldDeleteIconConfig(iconField) - iconField.iconConfig = {} - fs.remove(iconField.workpath .. MineOSInterface.iconConfigFileName, iconField.iconConfig) -end - ------------------------------------------------------------------------------------------------------------------------------------ - -MineOSInterface.iconLaunchers = {} - -function MineOSInterface.iconLaunchers.application(icon) - MineOSInterface.safeLaunch(icon.path .. "Main.lua") -end - -function MineOSInterface.iconLaunchers.directory(icon) - icon.parent.parent:setWorkpath(icon.path) -end - -function MineOSInterface.iconLaunchers.shortcut(icon) - local oldPath = icon.path - icon.path = icon.shortcutPath - icon:shortcutLaunch() - icon.path = oldPath -end - -function MineOSInterface.iconLaunchers.corrupted(icon) - GUI.alert("Application is corrupted") -end - -function MineOSInterface.iconLaunchers.extension(icon) - if icon.isShortcut then - MineOSInterface.safeLaunch(MineOSCore.properties.extensionAssociations[icon.shortcutExtension].launcher, icon.shortcutPath, "-o") - else - MineOSInterface.safeLaunch(MineOSCore.properties.extensionAssociations[icon.extension].launcher, icon.path, "-o") - end -end - -function MineOSInterface.iconLaunchers.script(icon) - MineOSInterface.launchScript(icon.path) -end - -function MineOSInterface.iconLaunchers.showPackageContent(icon) - icon.parent.parent:setWorkpath(icon.path) - icon.parent.parent:updateFileList() - icon.firstParent:draw() -end - -function MineOSInterface.iconLaunchers.showContainingFolder(icon) - icon.parent.parent:setWorkpath(fs.path(icon.shortcutPath)) - icon.parent.parent:updateFileList() - icon.firstParent:draw() -end - ------------------------------------------------------------------------------------------------------------------------------------ - -local function getCykaIconPosition(iconField) - local y = iconField.yOffset - for i = 1, #iconField.iconsContainer.children do - y = math.max(y, iconField.iconsContainer.children[i].localY) - end - - local x = iconField.xOffset - for i = 1, #iconField.iconsContainer.children do - if iconField.iconsContainer.children[i].localY == y then - x = math.max(x, iconField.iconsContainer.children[i].localX) - end - end - - x = x + MineOSCore.properties.iconWidth + MineOSCore.properties.iconHorizontalSpaceBetween - if x + MineOSCore.properties.iconWidth + MineOSCore.properties.iconHorizontalSpaceBetween > iconField.iconsContainer.width then - x, y = iconField.xOffset, y + MineOSCore.properties.iconHeight + MineOSCore.properties.iconVerticalSpaceBetween - end - - return x, y -end - -local function iconFieldUpdateFileList(iconField) - iconField.fileList = fs.sortedList(iconField.workpath, MineOSCore.properties.sortingMethod or "type", MineOSCore.properties.showHiddenFiles, iconField.filenameMatcher, false) - iconField:update() - - if iconField.iconConfigEnabled then - iconField:loadIconConfig() - end - - local configList, notConfigList = {}, {} - for i = iconField.fromFile, iconField.fromFile + iconField.iconCount.total - 1 do - if iconField.fileList[i] then - if iconField.iconConfigEnabled and iconField.iconConfig[iconField.fileList[i]] then - table.insert(configList, iconField.fileList[i]) - else - table.insert(notConfigList, iconField.fileList[i]) - end - else - break - end - end - - local function checkClipboard(icon) - if MineOSCore.clipboard and MineOSCore.clipboard.cut then - for i = 1, #MineOSCore.clipboard do - if MineOSCore.clipboard[i] == icon.path then - icon.cut = true - end - end - end - end - - -- Заполнение дочернего контейнера - iconField.iconsContainer:removeChildren() - for i = 1, #configList do - local icon = iconField.iconsContainer:addChild(MineOSInterface.icon( - iconField.iconConfig[configList[i]].x, - iconField.iconConfig[configList[i]].y, - iconField.workpath .. configList[i], - iconField.colors.text, - iconField.colors.selection - )) - - checkClipboard(icon) - icon.eventHandler = iconEventHandler - icon.launchers = iconField.launchers - icon:analyseExtension() - end - - local x, y - if #configList > 0 then - x, y = getCykaIconPosition(iconField, configList) - else - x, y = iconField.xOffset, iconField.yOffset - end - - for i = 1, #notConfigList do - local icon = iconField.iconsContainer:addChild(MineOSInterface.icon(x, y, iconField.workpath .. notConfigList[i], iconField.colors.text, iconField.colors.selection)) - iconField.iconConfig[notConfigList[i]] = {x = x, y = y} - - checkClipboard(icon) - icon.eventHandler = iconEventHandler - icon.launchers = iconField.launchers - icon:analyseExtension() - - x = x + MineOSCore.properties.iconWidth + MineOSCore.properties.iconHorizontalSpaceBetween - if x + MineOSCore.properties.iconWidth + MineOSCore.properties.iconHorizontalSpaceBetween - 1 > iconField.iconsContainer.width then - x, y = iconField.xOffset, y + MineOSCore.properties.iconHeight + MineOSCore.properties.iconVerticalSpaceBetween - end - end - - if iconField.iconConfigEnabled then - iconField:saveIconConfig() - end - - return iconField -end - -local function iconFieldBackgroundObjectEventHandler(application, object, e1, e2, e3, e4, e5, ...) - if e1 == "touch" then - if e5 == 0 then - object.parent:deselectAll() - object.parent.selection = { - x1 = e3, - y1 = e4 - } - - application:draw() - else - local menu = GUI.addContextMenu(MineOSInterface.application, e3, e4) - - local subMenu = menu:addSubMenu(MineOSCore.localization.create) - - subMenu:addItem(MineOSCore.localization.newFile).onTouch = function() - MineOSInterface.newFile(MineOSInterface.application, object.parent, e3, e4, object.parent.workpath) - end - - subMenu:addItem(MineOSCore.localization.newFolder).onTouch = function() - MineOSInterface.newFolder(MineOSInterface.application, object.parent, e3, e4, object.parent.workpath) - end - - subMenu:addItem(MineOSCore.localization.newFileFromURL, not component.isAvailable("internet")).onTouch = function() - MineOSInterface.newFileFromURL(MineOSInterface.application, object.parent, e3, e4, object.parent.workpath) - end - - subMenu:addSeparator() - - subMenu:addItem(MineOSCore.localization.newApplication).onTouch = function() - MineOSInterface.newApplication(MineOSInterface.application, object.parent, e3, e4, object.parent.workpath) - end - - menu:addSeparator() - - local subMenu = menu:addSubMenu(MineOSCore.localization.sortBy) - - subMenu:addItem(MineOSCore.localization.sortByName).onTouch = function() - object.parent:deleteIconConfig() - - MineOSCore.properties.sortingMethod = "name" - MineOSCore.saveProperties() - computer.pushSignal("MineOSCore", "updateFileList") - end - - subMenu:addItem(MineOSCore.localization.sortByDate).onTouch = function() - object.parent:deleteIconConfig() - - MineOSCore.properties.sortingMethod = "date" - MineOSCore.saveProperties() - computer.pushSignal("MineOSCore", "updateFileList") - end - - subMenu:addItem(MineOSCore.localization.sortByType).onTouch = function() - object.parent:deleteIconConfig() - - MineOSCore.properties.sortingMethod = "type" - MineOSCore.saveProperties() - computer.pushSignal("MineOSCore", "updateFileList") - end - - menu:addItem(MineOSCore.localization.sortAutomatically).onTouch = function() - object.parent:deleteIconConfig() - computer.pushSignal("MineOSCore", "updateFileList") - end - - menu:addItem(MineOSCore.localization.update).onTouch = function() - computer.pushSignal("MineOSCore", "updateFileList") - end - - menu:addSeparator() - - menu:addItem(MineOSCore.localization.paste, not MineOSCore.clipboard).onTouch = function() - local i = 1 - while i <= #MineOSCore.clipboard do - if fs.exists(MineOSCore.clipboard[i]) then - i = i + 1 - else - table.remove(MineOSCore.clipboard, i) - end - end - - MineOSInterface.copy(MineOSCore.clipboard, object.parent.workpath) - - if MineOSCore.clipboard.cut then - for i = 1, #MineOSCore.clipboard do - fs.remove(MineOSCore.clipboard[i]) - end - MineOSCore.clipboard = nil - end - - computer.pushSignal("MineOSCore", "updateFileList") - end - - MineOSInterface.application:draw() - end - elseif e1 == "drag" then - if object.parent.selection then - object.parent.selection.x2, object.parent.selection.y2 = e3, e4 - object:moveToFront() - - application:draw() - end - elseif e1 == "drop" then - object.parent.selection = nil - object:moveToBack() - - application:draw() - end -end - -local function iconFieldBackgroundObjectDraw(object) - if object.parent.selection and object.parent.selection.x2 then - local x1, y1, x2, y2 = object.parent.selection.x1, object.parent.selection.y1, object.parent.selection.x2, object.parent.selection.y2 - - if x2 < x1 then - x1, x2 = x2, x1 - end - - if y2 < y1 then - y1, y2 = y2, y1 - end - - if MineOSCore.properties.transparencyEnabled then - buffer.drawRectangle(x1, y1, x2 - x1 + 1, y2 - y1 + 1, object.parent.colors.selection, 0x0, " ", 0.5) - else - buffer.drawFrame(x1, y1, x2 - x1 + 1, y2 - y1 + 1, object.parent.colors.selection) - end - - for i = 1, #object.parent.iconsContainer.children do - local xCenter, yCenter = object.parent.iconsContainer.children[i].x + MineOSCore.properties.iconWidth / 2, object.parent.iconsContainer.children[i].y + MineOSCore.properties.iconHeight / 2 - object.parent.iconsContainer.children[i].selected = - xCenter >= x1 and - xCenter <= x2 and - yCenter >= y1 and - yCenter <= y2 - end - end -end - -local function iconFieldDeselectAll(iconField) - for i = 1, #iconField.iconsContainer.children do - iconField.iconsContainer.children[i].selected = false - end -end - -local function iconFieldGetSelectedIcons(iconField) - local selectedIcons = {} - - for i = 1, #iconField.iconsContainer.children do - if iconField.iconsContainer.children[i].selected then - table.insert(selectedIcons, iconField.iconsContainer.children[i]) - end - end - - return selectedIcons -end - -local function iconFieldSetWorkpath(iconField, path) - iconField.workpath = path - iconField.filenameMatcher = nil - iconField.fromFile = 1 - - return iconField -end - -function MineOSInterface.iconField(x, y, width, height, xOffset, yOffset, textColor, selectionColor, workpath) - local iconField = GUI.container(x, y, width, height) - - iconField.colors = { - text = textColor, - selection = selectionColor - } - - iconField.iconConfig = {} - iconField.iconCount = {} - iconField.fileList = {} - iconField.fromFile = 1 - iconField.iconConfigEnabled = false - iconField.xOffset = xOffset - iconField.yOffset = yOffset - iconField.workpath = workpath - iconField.filenameMatcher = nil - - iconField.backgroundObject = iconField:addChild(GUI.object(1, 1, width, height)) - iconField.backgroundObject.eventHandler = iconFieldBackgroundObjectEventHandler - iconField.backgroundObject.draw = iconFieldBackgroundObjectDraw - - iconField.iconsContainer = iconField:addChild(GUI.container(1, 1, width, height)) - - iconField.updateFileList = iconFieldUpdateFileList - iconField.update = iconFieldUpdate - iconField.deselectAll = iconFieldDeselectAll - iconField.loadIconConfig = iconFieldLoadIconConfig - iconField.saveIconConfig = iconFieldSaveIconConfig - iconField.deleteIconConfig = iconFieldDeleteIconConfig - iconField.getSelectedIcons = iconFieldGetSelectedIcons - iconField.setWorkpath = iconFieldSetWorkpath - - iconField.onLeftClick = MineOSInterface.iconLeftClick - iconField.onRightClick = MineOSInterface.iconRightClick - iconField.onDoubleClick = MineOSInterface.iconDoubleClick - - iconField.launchers = table.copy(MineOSInterface.iconLaunchers) - - return iconField -end - ----------------------------------------------------------------------------------------------------------------- - -function MineOSInterface.iconLeftClick(icon) - if not keyboard.isKeyDown(29) and not keyboard.isKeyDown(219) then - icon.parent.parent:deselectAll() - end - icon.selected = true - - MineOSInterface.application:draw() -end - -function MineOSInterface.iconDoubleClick(icon) - icon:launch() - icon.selected = false - MineOSInterface.application:draw() -end - -function MineOSInterface.iconRightClick(icon, e1, e2, e3, e4) - icon.selected = true - MineOSInterface.application:draw() - - local selectedIcons = icon.parent.parent:getSelectedIcons() - - local menu = GUI.addContextMenu(MineOSInterface.application, e3, e4) - - menu.onMenuClosed = function() - icon.parent.parent:deselectAll() - MineOSInterface.application:draw() - end - - if #selectedIcons == 1 then - if icon.isDirectory then - if icon.extension == ".app" then - menu:addItem(MineOSCore.localization.showPackageContent).onTouch = function() - icon.parent.parent.launchers.showPackageContent(icon) - end - - menu:addItem(MineOSCore.localization.launchWithArguments).onTouch = function() - MineOSInterface.launchWithArguments(MineOSInterface.application, icon.path .. "Main.lua") - end - - menu:addItem(MineOSCore.localization.edit .. " Main.lua").onTouch = function() - MineOSInterface.safeLaunch(MineOSPaths.editor, icon.path .. "Main.lua") - end - - menu:addSeparator() - end - - if icon.extension ~= ".app" then - menu:addItem(MineOSCore.localization.addToFavourites).onTouch = function() - local container = MineOSInterface.addBackgroundContainer(MineOSInterface.application, MineOSCore.localization.addToFavourites) - - local input = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x878787, 0xE1E1E1, 0x2D2D2D, icon.name, MineOSCore.localization.name)) - container.panel.eventHandler = function(application, object, e1) - if e1 == "touch" then - container:remove() - - if e1 == "touch" and #input.text > 0 then - computer.pushSignal("Finder", "updateFavourites", {name = input.text, path = icon.path}) - else - MineOSInterface.application:draw() - end - end - end - end - end - - else - if icon.isShortcut then - menu:addItem(MineOSCore.localization.editShortcut).onTouch = function() - MineOSInterface.editShortcut(MineOSInterface.application, icon.path) - computer.pushSignal("MineOSCore", "updateFileList") - end - - menu:addItem(MineOSCore.localization.showContainingFolder).onTouch = function() - icon.parent.parent.launchers.showContainingFolder(icon) - end - - menu:addSeparator() - else - if MineOSCore.properties.extensionAssociations[icon.extension] and MineOSCore.properties.extensionAssociations[icon.extension].contextMenu then - pcall(loadfile(MineOSCore.properties.extensionAssociations[icon.extension].contextMenu), icon, menu) - menu:addSeparator() - end - - -- local subMenu = menu:addSubMenu(MineOSCore.localization.openWith) - -- local fileList = fs.sortedList(MineOSPaths.applications, "name") - -- subMenu:addItem(MineOSCore.localization.select) - -- subMenu:addSeparator() - -- for i = 1, #fileList do - -- subMenu:addItem(fileList[i].nameWithoutExtension) - -- end - end - end - end - - if #selectedIcons > 1 then - menu:addItem(MineOSCore.localization.newFolderFromChosen .. " (" .. #selectedIcons .. ")").onTouch = function() - MineOSInterface.newFolderFromChosen(MineOSInterface.application, icon.parent.parent, e3, e4, selectedIcons) - end - menu:addSeparator() - end - - menu:addItem(MineOSCore.localization.archive .. (#selectedIcons > 1 and " (" .. #selectedIcons .. ")" or "")).onTouch = function() - local itemsToArchive = {} - for i = 1, #selectedIcons do - table.insert(itemsToArchive, selectedIcons[i].path) - end - - local success, reason = require("archive").pack(fs.path(icon.path) .. "/Archive.arc", itemsToArchive) - if not success then - GUI.alert(reason) - end - - computer.pushSignal("MineOSCore", "updateFileList") - end - - local function cutOrCopy(cut) - for i = 1, #icon.parent.children do - icon.parent.children[i].cut = nil - end - - MineOSCore.clipboard = {cut = cut} - for i = 1, #selectedIcons do - selectedIcons[i].cut = cut - table.insert(MineOSCore.clipboard, selectedIcons[i].path) - end - end - - menu:addItem(MineOSCore.localization.cut).onTouch = function() - cutOrCopy(true) - end - - menu:addItem(MineOSCore.localization.copy).onTouch = function() - cutOrCopy() - end - - if not icon.isShortcut or #selectedIcons > 1 then - local subMenu = menu:addSubMenu(MineOSCore.localization.createShortcut) - - subMenu:addItem(MineOSCore.localization.inCurrentDirectory).onTouch = function() - for i = 1, #selectedIcons do - if not selectedIcons[i].isShortcut then - MineOSCore.createShortcut( - fs.path(selectedIcons[i].path) .. "/" .. selectedIcons[i].nameWithoutExtension .. ".lnk", - selectedIcons[i].path - ) - end - end - - computer.pushSignal("MineOSCore", "updateFileList") - end - - subMenu:addItem(MineOSCore.localization.onDesktop).onTouch = function() - for i = 1, #selectedIcons do - if not selectedIcons[i].isShortcut then - MineOSCore.createShortcut( - MineOSPaths.desktop .. "/" .. selectedIcons[i].nameWithoutExtension .. ".lnk", - selectedIcons[i].path - ) - end - end - - computer.pushSignal("MineOSCore", "updateFileList") - end - end - - if #selectedIcons == 1 then - menu:addItem(MineOSCore.localization.rename).onTouch = function() - MineOSInterface.rename(MineOSInterface.application, icon.path) - end - end - - menu:addItem(MineOSCore.localization.delete).onTouch = function() - for i = 1, #selectedIcons do - if fs.path(selectedIcons[i].path) == MineOSPaths.trash then - fs.remove(selectedIcons[i].path) - else - local newName = MineOSPaths.trash .. selectedIcons[i].name - local clearName = selectedIcons[i].nameWithoutExtension - local repeats = 1 - while fs.exists(newName) do - newName, repeats = MineOSPaths.trash .. clearName .. string.rep("-copy", repeats) .. selectedIcons[i].extension, repeats + 1 - end - fs.rename(selectedIcons[i].path, newName) - end - end - - computer.pushSignal("MineOSCore", "updateFileList") - end - - menu:addSeparator() - - if #selectedIcons == 1 then - menu:addItem(MineOSCore.localization.addToDock).onTouch = function() - MineOSInterface.application.dockContainer.addIcon(icon.path).keepInDock = true - MineOSInterface.application.dockContainer.saveToOSSettings() - end - end - - menu:addItem(MineOSCore.localization.properties).onTouch = function() - for i = 1, #selectedIcons do - MineOSInterface.propertiesWindow(e3, e4, 40, selectedIcons[i]) - end - end - - MineOSInterface.application:draw() -end - ------------------------------------------------------------------------------------------------------------------------------------ - -function MineOSInterface.addBackgroundContainer(parentContainer, title) - local container = GUI.addBackgroundContainer(parentContainer, true, true, title) - container.panel.colors.background = MineOSCore.properties.transparencyEnabled and 0x0 or MineOSCore.properties.backgroundColor - container.panel.colors.transparency = MineOSCore.properties.transparencyEnabled and GUI.BACKGROUND_CONTAINER_PANEL_TRANSPARENCY - - return container -end - -local function addUniversalContainerWithInputTextBox(parentWindow, text, title, placeholder) - local container = MineOSInterface.addBackgroundContainer(parentWindow, title) - - container.inputField = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x878787, 0xE1E1E1, 0x2D2D2D, text, placeholder, false)) - container.label = container.layout:addChild(GUI.label(1, 1, 36, 1, 0xFF4940, MineOSCore.localization.file .. " " .. MineOSCore.localization.alreadyExists)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) - container.label.hidden = true - - return container -end - -local function checkFileToExists(container, path) - if fs.exists(path) then - container.label.hidden = false - container.parent:draw() - else - container:remove() - return true - end -end - -local function checkIconConfigCanSavePosition(iconField, x, y, filename) - if iconField.iconConfigEnabled then - iconField.iconConfig[filename] = { x = x, y = y } - iconField:saveIconConfig() - end -end - -function MineOSInterface.newFile(parentWindow, iconField, x, y, path) - local container = addUniversalContainerWithInputTextBox(parentWindow, nil, MineOSCore.localization.newFile, MineOSCore.localization.fileName) - - container.inputField.onInputFinished = function() - if checkFileToExists(container, path .. container.inputField.text) then - local file = io.open(path .. container.inputField.text, "w") - file:close() - checkIconConfigCanSavePosition(iconField, x, y, container.inputField.text) - MineOSInterface.safeLaunch(MineOSPaths.editor, path .. container.inputField.text) - computer.pushSignal("MineOSCore", "updateFileList") - end - end - - parentWindow:draw() -end - -function MineOSInterface.newFolder(parentWindow, iconField, x, y, path) - local container = addUniversalContainerWithInputTextBox(parentWindow, nil, MineOSCore.localization.newFolder, MineOSCore.localization.folderName) - - container.inputField.onInputFinished = function() - if checkFileToExists(container, path .. container.inputField.text) then - fs.makeDirectory(path .. container.inputField.text) - checkIconConfigCanSavePosition(iconField, x, y, container.inputField.text .. "/") - computer.pushSignal("MineOSCore", "updateFileList") - end - end - - parentWindow:draw() - - return container -end - -function MineOSInterface.newFileFromURL(parentWindow, iconField, x, y, path) - local container = addUniversalContainerWithInputTextBox(parentWindow, nil, "Загрузить файл по URL", MineOSCore.localization.fileName) - - container.inputFieldURL = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x878787, 0xE1E1E1, 0x2D2D2D, nil, "URL", false)) - container.panel.eventHandler = function(application, object, e1) - if e1 == "touch" then - if #container.inputField.text > 0 and #container.inputFieldURL.text > 0 then - if fs.exists(path .. container.inputField.text) then - container.label.hidden = false - application:draw() - else - container.layout:removeChildren(2) - container.layout:addChild(GUI.label(1, 1, container.width, 1, 0x787878, MineOSCore.localization.downloading .. "...")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) - application:draw() - - local success, reason = require("web").download(container.inputFieldURL.text, path .. container.inputField.text) - container:remove() - - if success then - checkIconConfigCanSavePosition(iconField, x, y, container.inputField.text) - computer.pushSignal("MineOSCore", "updateFileList") - else - GUI.alert(reason) - application:draw() - end - end - else - container:remove() - application:draw() - end - end - end - - parentWindow:draw() -end - -function MineOSInterface.newApplication(parentWindow, iconField, x, y, path) - local container = addUniversalContainerWithInputTextBox(parentWindow, nil, MineOSCore.localization.newApplication, MineOSCore.localization.applicationName) - - local filesystemChooser = container.layout:addChild(GUI.filesystemChooser(1, 1, 36, 3, 0xE1E1E1, 0x696969, 0x444444, 0x969696, nil, MineOSCore.localization.open, MineOSCore.localization.cancel, MineOSCore.localization.iconPath, "/")) - filesystemChooser:addExtensionFilter(".pic") - filesystemChooser:moveBackward() - - container.panel.eventHandler = function(application, object, e1) - if e1 == "touch" then - if #container.inputField.text > 0 then - local finalPath = path .. container.inputField.text .. ".app/" - if checkFileToExists(container, finalPath) then - fs.makeDirectory(finalPath) - fs.copy(filesystemChooser.path or MineOSPaths.icons .. "SampleIcon.pic", finalPath .. "Icon.pic") - - local file = io.open(finalPath .. "Main.lua", "w") - file:write("require(\"GUI\").alert(\"Hello world\")") - file:close() - - container:remove() - checkIconConfigCanSavePosition(iconField, x, y, container.inputField.text .. ".app/") - computer.pushSignal("MineOSCore", "updateFileList") - end - else - container:remove() - parentWindow:draw() - end - end - end - - parentWindow:draw() -end - -function MineOSInterface.newFolderFromChosen(parentWindow, iconField, x, y, selectedIcons) - local container = addUniversalContainerWithInputTextBox(parentWindow, nil, MineOSCore.localization.newFolderFromChosen .. " (" .. #selectedIcons .. ")", MineOSCore.localization.folderName) - - container.inputField.onInputFinished = function() - local path = fs.path(selectedIcons[1].path) .. container.inputField.text - if checkFileToExists(container, path) then - fs.makeDirectory(path) - for i = 1, #selectedIcons do - fs.rename(selectedIcons[i].path, path .. "/" .. selectedIcons[i].name) - end - - checkIconConfigCanSavePosition(iconField, x, y, container.inputField.text) - computer.pushSignal("MineOSCore", "updateFileList") - end - end - - parentWindow:draw() - - return container -end - -function MineOSInterface.rename(parentWindow, path) - local container = addUniversalContainerWithInputTextBox(parentWindow, fs.name(path), MineOSCore.localization.rename, MineOSCore.localization.newName) - - container.inputField.onInputFinished = function() - if checkFileToExists(container, fs.path(path) .. container.inputField.text) then - fs.rename(path, fs.path(path) .. container.inputField.text) - computer.pushSignal("MineOSCore", "updateFileList") - end - end - - parentWindow:draw() -end - -function MineOSInterface.editShortcut(parentWindow, path) - local text = MineOSCore.readShortcut(path) - local container = addUniversalContainerWithInputTextBox(parentWindow, text, MineOSCore.localization.editShortcut, MineOSCore.localization.rename) - - container.inputField.onInputFinished = function() - if fs.exists(container.inputField.text) then - MineOSCore.createShortcut(path, container.inputField.text) - container:remove() - computer.pushSignal("MineOSCore", "updateFileList") - else - container.label.text = MineOSCore.localization.shortcutIsCorrupted - container.label.hidden = false - MineOSInterface.application:draw() - end - end - - parentWindow:draw() -end - -function MineOSInterface.launchWithArguments(parentWindow, path, withTerminal) - local container = addUniversalContainerWithInputTextBox(parentWindow, nil, MineOSCore.localization.launchWithArguments) - - container.panel.eventHandler = function(application, object, e1) - if e1 == "touch" then - local args = {} - if container.inputField.text then - for arg in container.inputField.text:gmatch("[^%s]+") do - table.insert(args, arg) - end - end - - container:remove() - - if withTerminal then - MineOSInterface.clearTerminal() - if MineOSInterface.safeLaunch(path, table.unpack(args)) then - MineOSInterface.waitForPressingAnyKey() - end - else - MineOSInterface.safeLaunch(path, table.unpack(args)) - end - - parentWindow:draw(true) - end - end -end - ------------------------------------------ Windows patterns ----------------------------------------- - -function MineOSInterface.updateMenu() - local focusedWindow = MineOSInterface.application.windowsContainer.children[#MineOSInterface.application.windowsContainer.children] - MineOSInterface.application.menu.children = focusedWindow and focusedWindow.menu.children or MineOSInterface.menuInitialChildren -end - -function MineOSInterface.addWindow(window, preserveCoordinates) - -- Чекаем коорды - if not preserveCoordinates then - window.x, window.y = math.floor(MineOSInterface.application.windowsContainer.width / 2 - window.width / 2), math.floor(MineOSInterface.application.windowsContainer.height / 2 - window.height / 2) - end - - -- Ебурим окно к окнам - MineOSInterface.application.windowsContainer:addChild(window) - - -- Получаем путь залупы - local dockPath, info, dockIcon - for i = 0, math.huge do - info = debug.getinfo(i) - if info then - if info.source then - dockPath = info.source:match("=(.+%.app/)Main%.lua$") - if dockPath then - break - end - end - else - break - end - end - - dockPath = (dockPath or "/bin/OS.lua"):gsub("/+", "/") - - -- GUI.alert(dockPath) - - -- Чекаем наличие иконки в доке с таким же путем, и еси ее нет, то хуячим новую - for i = 1, #MineOSInterface.application.dockContainer.children do - if MineOSInterface.application.dockContainer.children[i].path == dockPath then - dockIcon = MineOSInterface.application.dockContainer.children[i] - break - end - end - if not dockIcon then - dockIcon = MineOSInterface.application.dockContainer.addIcon(dockPath, window) - end - - -- Ебурим ссылку на окна в иконку - dockIcon.windows = dockIcon.windows or {} - dockIcon.windows[window] = true - - -- Взалупливаем иконке индивидуальную менюху. По дефолту тут всякая хуйня и прочее - window.menu = GUI.menu(1, 1, 1) - window.menu.colors = MineOSInterface.application.menu.colors - local name = fs.hideExtension(fs.name(dockPath)) - local contextMenu = window.menu:addContextMenu(name, 0x0) - contextMenu:addItem(MineOSCore.localization.closeWindow .. " " .. name, false, "^W").onTouch = function() - window:close() - end - - -- Смещаем окно правее и ниже, если уже есть открытые окна этой софтины - local lastIndex - for i = #MineOSInterface.application.windowsContainer.children, 1, -1 do - if MineOSInterface.application.windowsContainer.children[i] ~= window and dockIcon.windows[MineOSInterface.application.windowsContainer.children[i]] then - lastIndex = i - break - end - end - if lastIndex then - window.localX, window.localY = MineOSInterface.application.windowsContainer.children[lastIndex].localX + 4, MineOSInterface.application.windowsContainer.children[lastIndex].localY + 2 - end - - -- Когда окно фокусицца, то главная ОСевая менюха заполницца ДЕТИШЕЧКАМИ оконной менюхи - window.onFocus = MineOSInterface.updateMenu - - -- Биндим функции по ресайзу/закрытию и прочему говнищу - window.close = function(window) - local sameIconExists = false - for i = 1, #MineOSInterface.application.dockContainer.children do - if - MineOSInterface.application.dockContainer.children[i].path == dockPath and - MineOSInterface.application.dockContainer.children[i].windows and - table.size(MineOSInterface.application.dockContainer.children[i].windows) > 1 - then - MineOSInterface.application.dockContainer.children[i].windows[window] = nil - sameIconExists = true - break - end - end - - if not sameIconExists then - dockIcon.windows = nil - if not dockIcon.keepInDock then - dockIcon:remove() - MineOSInterface.application.dockContainer.sort() - end - end - - window:remove() - MineOSInterface.updateMenu() - end - - -- Кнопочкам тоже эту хуйню пихаем - if window.actionButtons then - window.actionButtons.close.onTouch = function() - window.close(window) - end - window.actionButtons.maximize.onTouch = function() - window.maximize(window) - end - window.actionButtons.minimize.onTouch = function() - window.minimize(window) - end - end - - MineOSInterface.updateMenu() - - return MineOSInterface.application, window, window.menu -end - ------------------------------------------------------------------------------------------------------------------------------------ - -local function addKeyAndValue(window, x, y, key, value) - x = x + window:addChild(GUI.label(x, y, unicode.len(key) + 1, 1, 0x333333, key .. ":")).width + 1 - return window:addChild(GUI.label(x, y, unicode.len(value), 1, 0x555555, value)) -end - -function MineOSInterface.propertiesWindow(x, y, width, icon) - local application, window = MineOSInterface.addWindow(GUI.titledWindow(x, y, width, 1, package.loaded.MineOSCore.localization.properties)) - - window.backgroundPanel.colors.transparency = 0.2 - window:addChild(GUI.image(2, 3, icon.image)) - - local x, y = 11, 3 - addKeyAndValue(window, x, y, package.loaded.MineOSCore.localization.type, icon.extension and icon.extension or (icon.isDirectory and package.loaded.MineOSCore.localization.folder or package.loaded.MineOSCore.localization.unknown)); y = y + 1 - local fileSizeLabel = addKeyAndValue(window, x, y, package.loaded.MineOSCore.localization.size, icon.isDirectory and package.loaded.MineOSCore.localization.calculatingSize or string.format("%.2f", fs.size(icon.path) / 1024) .. " KB"); y = y + 1 - addKeyAndValue(window, x, y, package.loaded.MineOSCore.localization.date, os.date("%d.%m.%y, %H:%M", math.floor(fs.lastModified(icon.path) / 1000))); y = y + 1 - addKeyAndValue(window, x, y, package.loaded.MineOSCore.localization.path, " ") - - local textBox = window:addChild(GUI.textBox(17, y, window.width - 18, 1, nil, 0x555555, {icon.path}, 1, 0, 0, true, true)) - textBox.eventHandler = nil - - window.actionButtons.minimize:remove() - window.actionButtons.maximize:remove() - - window.height = textBox.y + textBox.height - window.backgroundPanel.width = window.width - window.backgroundPanel.height = textBox.y + textBox.height - - application:draw() - - if icon.isDirectory then - fileSizeLabel.text = string.format("%.2f", fs.directorySize(icon.path) / 1024) .. " KB" - application:draw() - end -end - ------------------------------------------------------------------------------------------------------------------------------------ - -local function GUICopy(application, fileList, toPath) - local applyYes, breakRecursion - - local container = MineOSInterface.addBackgroundContainer(application, MineOSCore.localization.copying) - local textBox = container.layout:addChild(GUI.textBox(1, 1, container.width, 1, nil, 0x787878, {}, 1, 0, 0, true, true):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)) - local switchAndLabel = container.layout:addChild(GUI.switchAndLabel(1, 1, 37, 8, 0x66DB80, 0x1E1E1E, 0xE1E1E1, 0x787878, MineOSCore.localization.applyToAll .. ":", false)) - container.panel.eventHandler = nil - - local buttonsLayout = container.layout:addChild(GUI.layout(1, 1, 1, 1, 1, 1)) - buttonsLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL) - buttonsLayout:setSpacing(1, 1, 2) - - buttonsLayout:addChild(GUI.button(1, 1, 11, 1, 0xE1E1E1, 0x2D2D2D, 0xA5A5A5, 0x2D2D2D, MineOSCore.localization.yes)).onTouch = function() - applyYes = true - application:stop() - end - buttonsLayout:addChild(GUI.button(1, 1, 11, 1, 0xE1E1E1, 0x2D2D2D, 0xA5A5A5, 0x2D2D2D, MineOSCore.localization.no)).onTouch = function() - application:stop() - end - buttonsLayout:addChild(GUI.button(1, 1, 11, 1, 0xE1E1E1, 0x2D2D2D, 0xA5A5A5, 0x2D2D2D, MineOSCore.localization.cancel)).onTouch = function() - breakRecursion = true - application:stop() - end - - buttonsLayout:fitToChildrenSize(1, 1) - - local function copyOrMove(path, finalPath) - switchAndLabel.hidden = true - buttonsLayout.hidden = true - - textBox.lines = { - MineOSCore.localization.copying .. " " .. MineOSCore.localization.faylaBlyad .. " " .. fs.name(path) .. " " .. MineOSCore.localization.toDirectory .. " " .. string.canonicalPath(toPath), - } - textBox:update() - - application:draw() - - fs.remove(finalPath) - fs.copy(path, finalPath) - end - - local function recursiveCopy(path, toPath) - local finalPath = toPath .. "/" .. fs.name(path) - - if fs.isDirectory(path) then - fs.makeDirectory(finalPath) - - for file in fs.list(path) do - if breakRecursion then - return - end - recursiveCopy(path .. "/" .. file, finalPath) - end - else - if fs.exists(finalPath) then - if not switchAndLabel.switch.state then - switchAndLabel.hidden = false - buttonsLayout.hidden = false - applyYes = false - - textBox.lines = { - MineOSCore.localization.file .. " " .. fs.name(path) .. " " .. MineOSCore.localization.alreadyExists .. " " .. MineOSCore.localization.inDirectory .. " " .. string.canonicalPath(toPath), - MineOSCore.localization.needReplace, - } - textBox:update() - - application:draw() - application:start() - application:draw() - end - - if applyYes then - copyOrMove(path, finalPath) - end - else - copyOrMove(path, finalPath) - end - end - end - - for i = 1, #fileList do - recursiveCopy(fileList[i], toPath) - end - - container:remove() - application:draw() -end - -function MineOSInterface.copy(what, toPath) - if type(what) == "string" then - what = {what} - end - - GUICopy(MineOSInterface.application, what, toPath) -end - -local function menuWidgetEventHandler(application, object, e1, ...) - if e1 == "touch" and object.onTouch then - object.selected = true - MineOSInterface.application:draw() - - object.onTouch(application, object, e1, ...) - - object.selected = false - MineOSInterface.application:draw() - end -end - -local function menuWidgetDraw(object) - if object.selected then - object.textColor = 0xFFFFFF - buffer.drawRectangle(object.x - 1, object.y, object.width + 2, 1, 0x3366CC, object.textColor, " ") - else - object.textColor = 0x0 - end - - object.drawContent(object) -end - -function MineOSInterface.menuWidget(width) - local object = GUI.object(1, 1, width, 1) - - object.selected = false - object.eventHandler = menuWidgetEventHandler - object.draw = menuWidgetDraw - - return object -end - -function MineOSInterface.addMenuWidget(object) - MineOSInterface.application.menuLayout:addChild(object) - object:moveToBack() - - return object -end - ------------------------------------------------------------------------------------------------------------------------------------ - -function MineOSInterface.showErrorWindow(path, line, traceback) - local application = GUI.application(1, 1, buffer.getWidth(), math.floor(buffer.getHeight() * 0.5)) - application.y = math.floor(buffer.getHeight() / 2 - application.height / 2) - - application:addChild(GUI.panel(1, 1, application.width, 3, 0x383838)) - application:addChild(GUI.label(1, 2, application.width, 1, 0xFFFFFF, MineOSCore.localization.errorWhileRunningProgram .. "\"" .. fs.name(path) .. "\"")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP) - local actionButtons = application:addChild(GUI.actionButtons(2, 2, false)) - local sendToDeveloperButton = application:addChild(GUI.adaptiveButton(9, 1, 2, 1, 0x444444, 0xFFFFFF, 0x343434, 0xFFFFFF, MineOSCore.localization.sendFeedback)) - - local codeView = application:addChild(GUI.codeView(1, 4, math.floor(application.width * 0.62), application.height - 3, 1, 1, 100, {}, {[line] = 0xFF4444}, GUI.LUA_SYNTAX_PATTERNS, GUI.LUA_SYNTAX_COLOR_SCHEME, true, {})) - - codeView.fromLine = line - math.floor((application.height - 3) / 2) + 1 - if codeView.fromLine <= 0 then - codeView.fromLine = 1 - end - local toLine, lineCounter = codeView.fromLine + codeView.height - 1, 1 - - for line in io.lines(path) do - if lineCounter >= codeView.fromLine and lineCounter <= toLine then - codeView.lines[lineCounter] = string.gsub(line, " ", " ") - elseif lineCounter < codeView.fromLine then - codeView.lines[lineCounter] = " " - elseif lineCounter > toLine then - break - end - lineCounter = lineCounter + 1 - if lineCounter % 200 == 0 then - computer.pullSignal(0) - end - end - - application:addChild(GUI.textBox(codeView.width + 1, 4, application.width - codeView.width, codeView.height, 0xFFFFFF, 0x0, string.wrap(MineOSCore.parseErrorMessage(traceback, 4), application.width - codeView.width - 2), 1, 1, 0)) - - actionButtons.close.onTouch = function() - application:stop() - end - - application.eventHandler = function(application, object, e1, e2, e3, e4) - if e1 == "key_down" and e4 == 28 then - actionButtons.close.onTouch() - end - end - - sendToDeveloperButton.onTouch = function() - if component.isAvailable("internet") then - local url = "https://api.mcmodder.ru/ECS/report.php?path=" .. path .. "&errorMessage=" .. string.optimizeForURLRequests(traceback) - local success, reason = component.internet.request(url) - if success then - success:close() - end - - sendToDeveloperButton.text = MineOSCore.localization.sendedFeedback - application:draw() - os.sleep(1) - end - - actionButtons.close.onTouch() - end - - buffer.clear(0x0, 0.5) - application:draw() - - for i = 1, 3 do - component.computer.beep(1500, 0.08) - end - - application:start() -end - -function MineOSInterface.safeLaunch(...) - local success, path, line, traceback = MineOSCore.safeLaunch(...) - if not success then - MineOSInterface.showErrorWindow(path, line, traceback) - end - - return success, path, line, traceback -end - ------------------------------------------------------------------------------------------------------------------------------------ - -local overrideGUIDropDownMenu = GUI.dropDownMenu -function MineOSInterface.updateColorScheme() - GUI.dropDownMenu = function(...) - local menu = overrideGUIDropDownMenu(...) - - menu.colors.transparency.background = MineOSCore.properties.transparencyEnabled and GUI.CONTEXT_MENU_BACKGROUND_TRANSPARENCY - menu.colors.transparency.shadow = MineOSCore.properties.transparencyEnabled and GUI.CONTEXT_MENU_SHADOW_TRANSPARENCY - - return menu - end -end - -MineOSInterface.updateColorScheme() - -MineOSInterface.cacheIconSource("folder", MineOSPaths.icons .. "Folder.pic") -MineOSInterface.cacheIconSource("fileNotExists", MineOSPaths.icons .. "FileNotExists.pic") -MineOSInterface.cacheIconSource("application", MineOSPaths.icons .. "Application.pic") -MineOSInterface.cacheIconSource("trash", MineOSPaths.icons .. "Trash.pic") -MineOSInterface.cacheIconSource("script", MineOSPaths.icons .. "Script.pic") - ------------------------------------------------------------------------------------------------------------------------------------ - -return MineOSInterface - \ No newline at end of file diff --git a/lib/MineOSPaths.lua b/lib/MineOSPaths.lua deleted file mode 100755 index 6e52ae6e..00000000 --- a/lib/MineOSPaths.lua +++ /dev/null @@ -1,36 +0,0 @@ - -local filesystem = require("filesystem") -local MineOSPaths = {} - ---------------------------------------------------------------------------- - -MineOSPaths.OS = "/MineOS/" -MineOSPaths.downloads = MineOSPaths.OS .. "Downloads/" -MineOSPaths.system = MineOSPaths.OS .. "System/" -MineOSPaths.applicationData = MineOSPaths.system .. "Application data/" -MineOSPaths.extensionAssociations = MineOSPaths.system .. "Extensions/" -MineOSPaths.localizationFiles = MineOSPaths.system .. "Localizations/" -MineOSPaths.icons = MineOSPaths.system .. "Icons/" -MineOSPaths.applications = MineOSPaths.OS .. "Applications/" -MineOSPaths.pictures = MineOSPaths.OS .. "Pictures/" -MineOSPaths.desktop = MineOSPaths.OS .. "Desktop/" -MineOSPaths.fileVersions = MineOSPaths.system .. "File versions.cfg" -MineOSPaths.trash = MineOSPaths.OS .. "Trash/" -MineOSPaths.properties = MineOSPaths.system .. "Properties.cfg" -MineOSPaths.editor = MineOSPaths.applications .. "/MineCode IDE.app/Main.lua" -MineOSPaths.explorer = MineOSPaths.applications .. "/Finder.app/Main.lua" -MineOSPaths.imageEditor = MineOSPaths.applications .. "/Picture Edit.app/Main.lua" -MineOSPaths.temporary = MineOSPaths.system .. "Temporary/" -MineOSPaths.network = MineOSPaths.system .. "Network/" - ---------------------------------------------------------------------------- - -filesystem.makeDirectory(MineOSPaths.pictures) -filesystem.makeDirectory(MineOSPaths.applicationData) -filesystem.makeDirectory(MineOSPaths.trash) -filesystem.makeDirectory(MineOSPaths.desktop) -filesystem.makeDirectory(MineOSPaths.network) - ---------------------------------------------------------------------------- - -return MineOSPaths \ No newline at end of file diff --git a/lib/SHA2.lua b/lib/SHA2.lua deleted file mode 100644 index a2420c7a..00000000 --- a/lib/SHA2.lua +++ /dev/null @@ -1,243 +0,0 @@ --- SHA-256 code in Lua 5.2; based on the pseudo-code from --- Wikipedia (http://en.wikipedia.org/wiki/SHA-2) - -local bit32 = require("bit32") - -local band, rrotate, bxor, rshift, bnot = - bit32.band, bit32.rrotate, bit32.bxor, bit32.rshift, bit32.bnot - -local string, setmetatable, assert = string, setmetatable, assert - -_ENV = nil - --- Initialize table of round constants --- (first 32 bits of the fractional parts of the cube roots of the first --- 64 primes 2..311): -local k = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, -} - - --- transform a string of bytes in a string of hexadecimal digits -local function str2hexa (s) - local h = string.gsub(s, ".", function(c) - return string.format("%02x", string.byte(c)) - end) - return h -end - - --- transform number 'l' in a big-endian sequence of 'n' bytes --- (coded as a string) -local function num2s (l, n) - local s = "" - for i = 1, n do - local rem = l % 256 - s = string.char(rem) .. s - l = (l - rem) / 256 - end - return s -end - --- transform the big-endian sequence of four bytes starting at --- index 'i' in 's' into a number -local function s232num (s, i) - local n = 0 - for i = i, i + 3 do - n = n*256 + string.byte(s, i) - end - return n -end - - --- append the bit '1' to the message --- append k bits '0', where k is the minimum number >= 0 such that the --- resulting message length (in bits) is congruent to 448 (mod 512) --- append length of message (before pre-processing), in bits, as 64-bit --- big-endian integer -local function preproc (msg, len) - local extra = -(len + 1 + 8) % 64 - len = num2s(8 * len, 8) -- original len in bits, coded - msg = msg .. "\128" .. string.rep("\0", extra) .. len - assert(#msg % 64 == 0) - return msg -end - - -local function initH224 (H) - -- (second 32 bits of the fractional parts of the square roots of the - -- 9th through 16th primes 23..53) - H[1] = 0xc1059ed8 - H[2] = 0x367cd507 - H[3] = 0x3070dd17 - H[4] = 0xf70e5939 - H[5] = 0xffc00b31 - H[6] = 0x68581511 - H[7] = 0x64f98fa7 - H[8] = 0xbefa4fa4 - return H -end - - -local function initH256 (H) - -- (first 32 bits of the fractional parts of the square roots of the - -- first 8 primes 2..19): - H[1] = 0x6a09e667 - H[2] = 0xbb67ae85 - H[3] = 0x3c6ef372 - H[4] = 0xa54ff53a - H[5] = 0x510e527f - H[6] = 0x9b05688c - H[7] = 0x1f83d9ab - H[8] = 0x5be0cd19 - return H -end - - -local function digestblock (msg, i, H) - - -- break chunk into sixteen 32-bit big-endian words w[1..16] - local w = {} - for j = 1, 16 do - w[j] = s232num(msg, i + (j - 1)*4) - end - - -- Extend the sixteen 32-bit words into sixty-four 32-bit words: - for j = 17, 64 do - local v = w[j - 15] - local s0 = bxor(rrotate(v, 7), rrotate(v, 18), rshift(v, 3)) - v = w[j - 2] - local s1 = bxor(rrotate(v, 17), rrotate(v, 19), rshift(v, 10)) - w[j] = w[j - 16] + s0 + w[j - 7] + s1 - end - - -- Initialize hash value for this chunk: - local a, b, c, d, e, f, g, h = - H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] - - -- Main loop: - for i = 1, 64 do - local s0 = bxor(rrotate(a, 2), rrotate(a, 13), rrotate(a, 22)) - local maj = bxor(band(a, b), band(a, c), band(b, c)) - local t2 = s0 + maj - local s1 = bxor(rrotate(e, 6), rrotate(e, 11), rrotate(e, 25)) - local ch = bxor (band(e, f), band(bnot(e), g)) - local t1 = h + s1 + ch + k[i] + w[i] - - h = g - g = f - f = e - e = d + t1 - d = c - c = b - b = a - a = t1 + t2 - end - - -- Add (mod 2^32) this chunk's hash to result so far: - H[1] = band(H[1] + a) - H[2] = band(H[2] + b) - H[3] = band(H[3] + c) - H[4] = band(H[4] + d) - H[5] = band(H[5] + e) - H[6] = band(H[6] + f) - H[7] = band(H[7] + g) - H[8] = band(H[8] + h) - -end - - -local function finalresult224 (H) - -- Produce the final hash value (big-endian): - return - str2hexa(num2s(H[1], 4)..num2s(H[2], 4)..num2s(H[3], 4)..num2s(H[4], 4).. - num2s(H[5], 4)..num2s(H[6], 4)..num2s(H[7], 4)) -end - - -local function finalresult256 (H) - -- Produce the final hash value (big-endian): - return - str2hexa(num2s(H[1], 4)..num2s(H[2], 4)..num2s(H[3], 4)..num2s(H[4], 4).. - num2s(H[5], 4)..num2s(H[6], 4)..num2s(H[7], 4)..num2s(H[8], 4)) -end - - ----------------------------------------------------------------------- -local HH = {} -- to reuse - -local function hash224 (msg) - msg = preproc(msg, #msg) - local H = initH224(HH) - - -- Process the message in successive 512-bit (64 bytes) chunks: - for i = 1, #msg, 64 do - digestblock(msg, i, H) - end - - return finalresult224(H) -end - - -local function hash256 (msg) - msg = preproc(msg, #msg) - local H = initH256(HH) - - -- Process the message in successive 512-bit (64 bytes) chunks: - for i = 1, #msg, 64 do - digestblock(msg, i, H) - end - - return finalresult256(H) -end ----------------------------------------------------------------------- --- local mt = {} - --- local function new256 () --- local o = {H = initH256({}), msg = "", len = 0} --- setmetatable(o, mt) --- return o --- end - --- mt.__index = mt - --- function mt:add (m) --- self.msg = self.msg .. m --- self.len = self.len + #m --- local t = 0 --- while #self.msg - t >= 64 do --- digestblock(self.msg, t + 1, self.H) --- t = t + 64 --- end --- self.msg = self.msg:sub(t + 1, -1) --- end - - --- function mt:close () --- self.msg = preproc(self.msg, self.len) --- self:add("") --- return finalresult256(self.H) --- end ----------------------------------------------------------------------- - -return { - hash = hash256 - -- hash256 = hash256, - -- hash224 = hash224, - -- new256 = new256, -} \ No newline at end of file diff --git a/lib/base64.lua b/lib/base64.lua deleted file mode 100755 index 8916fdbc..00000000 --- a/lib/base64.lua +++ /dev/null @@ -1,39 +0,0 @@ - -local bit32 = require("bit32") -local base64 = {} - --------------------------------------------------------------------------------- - -function base64.encode(data) - data = {string.byte(data, 1, #data)} - local result, dataIndex, resultIndex, chars, bit32Bor, bit32Rshift, bit32Lshift, stringChar, stringSub = {}, 1, 1, {[0] = "A", [1] = "B", [2] = "C", [3] = "D", [4] = "E", [5] = "F", [6] = "G", [7] = "H", [8] = "I", [9] = "J", [10] = "K", [11] = "L", [12] = "M", [13] = "N", [14] = "O", [15] = "P", [16] = "Q", [17] = "R", [18] = "S", [19] = "T", [20] = "U", [21] = "V", [22] = "W", [23] = "X", [24] = "Y", [25] = "Z", [26] = "a", [27] = "b", [28] = "c", [29] = "d", [30] = "e", [31] = "f", [32] = "g", [33] = "h", [34] = "i", [35] = "j", [36] = "k", [37] = "l", [38] = "m", [39] = "n", [40] = "o", [41] = "p", [42] = "q", [43] = "r", [44] = "s", [45] = "t", [46] = "u", [47] = "v", [48] = "w", [49] = "x", [50] = "y", [51] = "z", [52] = "0", [53] = "1", [54] = "2", [55] = "3", [56] = "4", [57] = "5", [58] = "6", [59] = "7", [60] = "8", [61] = "9", [62] = "+", [63] = "/"}, bit32.bor, bit32.rshift, bit32.lshift, string.byte, string.sub - - for i = 1, #data, 3 do - result[resultIndex] = chars[bit32Rshift(data[dataIndex] or 0, 2)] or "="; resultIndex = resultIndex + 1 - result[resultIndex] = chars[bit32Bor(bit32Lshift((data[dataIndex] or 0) % 4, 4), bit32Rshift(data[dataIndex + 1] or 0, 4))] or "="; resultIndex = resultIndex + 1 - result[resultIndex] = #data - i > 0 and chars[bit32Bor(bit32Lshift((data[dataIndex + 1] or 0) % 16, 2), bit32Rshift(data[dataIndex + 2] or 0, 6))] or "="; resultIndex = resultIndex + 1 - result[resultIndex] = #data - i > 1 and chars[(data[dataIndex + 2] or 0) % 64] or "="; resultIndex = resultIndex + 1 - - dataIndex = dataIndex + 3 - end - - return table.concat(result) -end - -function base64.decode(data) - local result, resultIndex, bytes, bit32Bor, bit32Rshift, bit32Lshift, stringChar, stringSub, byte1, byte2, byte3, byte4 = {}, 1, {["A"] = 0, ["B"] = 1, ["C"] = 2, ["D"] = 3, ["E"] = 4, ["F"] = 5, ["G"] = 6, ["H"] = 7, ["I"] = 8, ["J"] = 9, ["K"] = 10, ["L"] = 11, ["M"] = 12, ["N"] = 13, ["O"] = 14, ["P"] = 15, ["Q"] = 16, ["R"] = 17, ["S"] = 18, ["T"] = 19, ["U"] = 20, ["V"] = 21, ["W"] = 22, ["X"] = 23, ["Y"] = 24, ["Z"] = 25, ["a"] = 26, ["b"] = 27, ["c"] = 28, ["d"] = 29, ["e"] = 30, ["f"] = 31, ["g"] = 32, ["h"] = 33, ["i"] = 34, ["j"] = 35, ["k"] = 36, ["l"] = 37, ["m"] = 38, ["n"] = 39, ["o"] = 40, ["p"] = 41, ["q"] = 42, ["r"] = 43, ["s"] = 44, ["t"] = 45, ["u"] = 46, ["v"] = 47, ["w"] = 48, ["x"] = 49, ["y"] = 50, ["z"] = 51, ["0"] = 52, ["1"] = 53, ["2"] = 54, ["3"] = 55, ["4"] = 56, ["5"] = 57, ["6"] = 58, ["7"] = 59, ["8"] = 60, ["9"] = 61, ["+"] = 62, ["/"] = 63, ["="] = nil}, bit32.bor, bit32.rshift, bit32.lshift, string.char, string.sub - - for i = 1, #data, 4 do - byte1, byte2, byte3, byte4 = bytes[stringSub(data, i, i)], bytes[stringSub(data, i + 1, i + 1)], bytes[stringSub(data, i + 2, i + 2)], bytes[stringSub(data, i + 3, i + 3)] - - result[resultIndex] = stringChar(bit32Bor(bit32Lshift(byte1, 2) % 256, bit32Rshift(byte2, 4))); resultIndex = resultIndex + 1 - result[resultIndex] = byte3 and stringChar(bit32Bor(bit32Lshift(byte2, 4) % 256, bit32Rshift(byte3, 2))) or ""; resultIndex = resultIndex + 1 - result[resultIndex] = byte4 and stringChar(bit32Bor(bit32Lshift(byte3, 6) % 256, byte4)) or ""; resultIndex = resultIndex + 1 - end - - return table.concat(result) -end - --------------------------------------------------------------------------------- - -return base64 \ No newline at end of file diff --git a/lib/context.lua b/lib/context.lua deleted file mode 100644 index 6c1abef9..00000000 --- a/lib/context.lua +++ /dev/null @@ -1,137 +0,0 @@ -local component = require("component") -local event = require("event") -local unicode = require("unicode") -local ecs = require("ECSAPI") -local gpu = component.gpu - -local context = {} - -local separatorColor = 0xcccccc -local shortcutAutism = 3 - ----------------------------------------------------------------------------------------------------------------- - ---ОБЪЕКТЫ -local obj = {} -local function newObj(class, name, ...) - obj[class] = obj[class] or {} - obj[class][name] = {...} -end - -function context.menu(x, y, ...) - - local data = {...} - local sData = #data - - obj = {} - - --Получаем размер экрана - local xSize, ySize = gpu.getResolution() - - --Получаем самую жирную полоску текста - local biggestElement = 0 - for i = 1, sData do - if data[i] ~= "-" then - local length = unicode.len(data[i][1]) - if data[i][3] then length = length + shortcutAutism + unicode.len(data[i][3]) end - biggestElement = math.max(biggestElement, length) - length = nil - end - end - - --Задание ширины и высоты - local width = 4 + biggestElement - local height = sData - - --А это чтоб за края экрана не лезло - if y + height >= ySize then y = ySize - height end - if x + width + 1 >= xSize then x = xSize - width - 1 end - - --Рисуем окошечко и запоминаем, че было до него (сначала был Бог...!) - local oldPixels = ecs.rememberOldPixels(x, y, x + width + 1, y + height) - ecs.square(x, y, width, height, 0xffffff) - ecs.windowShadow(x, y, width, height) - gpu.setBackground(0xffffff) - - --Нарисовать конкретный элемент - local function drawElement(i, background, foreground, yPos) - - if background then ecs.square(x, yPos, width, 1, background) end - - --Получаем текстик - local text - if data[i] == "-" then - ecs.colorText(x, yPos, separatorColor, string.rep("─", width)) - else - --Нужный цвет - local color = foreground or 0x000000 - if data[i][2] then color = separatorColor end - - --Рисуем текстик - ecs.colorText(x + 2, yPos, color, data[i][1]) - - --Рисуем сокращение - if data[i][3] then gpu.set(x + width - 2 - unicode.len(data[i][3]), yPos, data[i][3]) end - - if not data[i][2] then newObj("Elements", i, x, yPos, x + width - 1, yPos) end - end - end - - --Рисуем все элементы - local counter = 0 - local yPos - for i = 1, sData do - yPos = y + counter - - drawElement(i, nil, nil, yPos) - - counter = counter + 1 - end - - --Проверка нажатия - local action - local e = {event.pull("touch")} - if obj["Elements"] then - for key, val in pairs(obj["Elements"]) do - if ecs.clickedAtArea(e[3], e[4], obj["Elements"][key][1], obj["Elements"][key][2], obj["Elements"][key][3], obj["Elements"][key][4]) then - - --ecs.error("Кол-во объектов: "..#obj["Elements"]..", кликнули на объект номер = "..tostring(key)..", #Data = "..#data..", sData ="..sData) - - drawElement(key, ecs.colors.blue, 0xffffff, e[4]) - os.sleep(0.3) - action = data[key][1] - break - end - end - end - - --Красим то, че было - ecs.drawOldPixels(oldPixels) - - --Возвращаем выбранное - return action - -end - ----------------------------------------------------------------------------------------------------------------- - --- while true do --- local e = {event.pull("touch")} --- ecs.prepareToExit() --- local action = context.menu(e[3], e[4], {"Показать содержимое"}, "-", {"Вырезать", false, "^X"}, {"Копировать", false, "^C"}, {"Вставить", true, "^V"}, "-", {"Переименовать"}, {"Создать ярлык"}, {"Добавить в архив"}, "-", {"Удалить", false, "⌫"}) --- ecs.prepareToExit() --- print("Ты выбрал = "..tostring(action)) --- end - ---local action = context.menu(6, 2, {(function() if showHiddenFiles then return "Скрывать скрытые файлы" else return "Показывать скрытые файлы" end end)()}, {(function() if showSystemFiles then return "Скрывать системные файлы" else return "Показывать системные файлы" end end)()}, "-", {(function() if showFileFormat then return "Скрывать формат файлов" else return "Показывать формат файлов" end end)()}) - - -return context - - - - - - - - diff --git a/lib/crc32lua.lua b/lib/crc32lua.lua deleted file mode 100644 index b3490f61..00000000 --- a/lib/crc32lua.lua +++ /dev/null @@ -1,208 +0,0 @@ ---[[ - -LUA MODULE - - digest.crc32 - CRC-32 checksum implemented entirely in Lua. - -SYNOPSIS - - local CRC = require 'digest.crc32lua' - print(CRC.crc32 'test') --> 0xD87F7E0C or -662733300 - - assert(CRC.crc32('st', CRC.crc32('te')) == CRC.crc32 'test') - -DESCRIPTION - - This can be used to compute CRC-32 checksums on strings. - This is similar to [1-2]. - -API - - Note: in the functions below, checksums are 32-bit integers stored in - numbers. The number format currently depends on the bit - implementation--see DESIGN NOTES below. - - CRC.crc32_byte(byte [, crc]) --> rcrc - - Returns CRC-32 checksum `rcrc` of byte `byte` (number 0..255) appended to - a string with CRC-32 checksum `crc`. `crc` defaults to 0 (empty string) - if omitted. - - CRC.crc32_string(s, crc) --> bcrc - - Returns CRC-32 checksum `rcrc` of string `s` appended to - a string with CRC-32 checksum `crc`. `crc` defaults to 0 (empty string) - if omitted. - - CRC.crc32(o, crc) --> bcrc - - This invokes `crc32_byte` if `o` is a byte or `crc32_string` if `o` - is a string. - - CRC.bit - - This contains the underlying bit library used by the module. It - should be considered a read-only copy. - -DESIGN NOTES - - Currently, this module exposes the underlying bit array implementation in CRC - checksums returned. In BitOp, bit arrays are 32-bit signed integer numbers - (may be negative). In Lua 5.2 'bit32' and 'bit.numberlua', bit arrays are - 32-bit unsigned integer numbers (non-negative). This is subject to change - in the future but is currently done due to (unconfirmed) performance - implications. - - On platforms with 64-bit numbers, one way to normalize CRC - checksums to be unsigned is to do `crcvalue % 2^32`, - - The name of this module is inspired by Perl `Digest::CRC*`. - -DEPENDENCIES - - Requires one of the following bit libraries: - - BitOp "bit" -- bitop.luajit.org -- This is included in LuaJIT and also available - for Lua 5.1/5.2. This provides the fastest performance in LuaJIT. - Lua 5.2 "bit32" -- www.lua.org/manual/5.2 -- This is provided in Lua 5.2 - and is preferred in 5.2 (unless "bit" also happens to be installed). - "bit.numberlua" (>=000.003) -- https://github.com/davidm/lua-bit-numberlua - This is slowest and used as a last resort. - It is only a few times slower than "bit32" though. - -DOWNLOAD/INSTALLATION - - If using LuaRocks: - luarocks install lua-digest-crc32lua - - Otherwise, download . - Alternately, if using git: - git clone git://github.com/davidm/lua-digest-crc32lua.git - cd lua-digest-crc32lua - Optionally unpack: - ./util.mk - or unpack and install in LuaRocks: - ./util.mk install - -REFERENCES - - [1] http://www.axlradius.com/freestuff/CRC32.java - [2] http://www.gamedev.net/reference/articles/article1941.asp - [3] http://java.sun.com/j2se/1.5.0/docs/api/java/util/zip/CRC32.html - [4] http://www.dsource.org/projects/tango/docs/current/tango.io.digest.Crc32.html - [5] http://pydoc.org/1.5.2/zlib.html#-crc32 - [6] http://www.python.org/doc/2.5.2/lib/module-binascii.html - -LICENSE - - (c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT). - - 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. - (end license) - ---]] - - -local M = {_TYPE='module', _NAME='crc32lua', _VERSION='0.3.20111128'} - -local type = type -local require = require -local setmetatable = setmetatable - ---[[ - Requires the first module listed that exists, else raises like `require`. - If a non-string is encountered, it is returned. - Second return value is module name loaded (or ''). - --]] -local function requireany(...) - local errs = {} - for _,name in ipairs{...} do - if type(name) ~= 'string' then return name, '' end - local ok, mod = pcall(require, name) - if ok then return mod, name end - errs[#errs+1] = mod - end - error(table.concat(errs, '\n'), 2) -end - -local bit, name_ = requireany('bit', 'bit32', 'bit.numberlua') -local bxor = bit.bxor -local bnot = bit.bnot -local band = bit.band -local rshift = bit.rshift - --- CRC-32-IEEE 802.3 (V.42) -local POLY = 0xEDB88320 - --- Memoize function pattern (like http://lua-users.org/wiki/FuncTables ). -local function memoize(f) - local mt = {} - local t = setmetatable({}, mt) - function mt:__index(k) - local v = f(k); t[k] = v - return v - end - return t -end - --- CRC table. -local crc_table = memoize(function(i) - local crc = i - for _=1,8 do - local b = band(crc, 1) - crc = rshift(crc, 1) - if b == 1 then crc = bxor(crc, POLY) end - end - return crc -end) - - -function M.crc32_byte(byte, crc) - crc = bnot(crc or 0) - local v1 = rshift(crc, 8) - local v2 = crc_table[bxor(crc % 256, byte)] - return bnot(bxor(v1, v2)) -end -local M_crc32_byte = M.crc32_byte - - -function M.crc32_string(s, crc) - crc = crc or 0 - for i=1,#s do - crc = M_crc32_byte(s:byte(i), crc) - end - return crc -end -local M_crc32_string = M.crc32_string - - -function M.crc32(s, crc) - if type(s) == 'string' then - return M_crc32_string(s, crc) - else - return M_crc32_byte(s, crc) - end -end - - -M.bit = bit -- bit library used - - -return M - diff --git a/lib/deflatelua.lua b/lib/deflatelua.lua deleted file mode 100644 index a6153d9a..00000000 --- a/lib/deflatelua.lua +++ /dev/null @@ -1,700 +0,0 @@ ---[[ - -LUA MODULE - - deflatelua - inflate (and gunzip/zlib) implemented in Lua. - -DESCRIPTION - - This is a pure Lua implementation of decompressing the DEFLATE format, - including the related zlib and gzip formats. - - Note: This library only supports decompression. - Compression is not currently implemented. - -REFERENCES - - [1] DEFLATE Compressed Data Format Specification version 1.3 - http://tools.ietf.org/html/rfc1951 - [2] GZIP file format specification version 4.3 - http://tools.ietf.org/html/rfc1952 - [3] http://en.wikipedia.org/wiki/DEFLATE - [4] pyflate, by Paul Sladen - http://www.paul.sladen.org/projects/pyflate/ - [5] Compress::Zlib::Perl - partial pure Perl implementation of - Compress::Zlib - http://search.cpan.org/~nwclark/Compress-Zlib-Perl/Perl.pm - -LICENSE - - (c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT). - - 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. - (end license) - - - - Modified by TehSomeLuigi under the above license. - ---]] - -local M = {_TYPE='module', _NAME='deflatelua', _VERSION='0.3.20111128'} - -local assert = assert -local error = error -local ipairs = ipairs -local pairs = pairs -local print = print -local require = require -local tostring = tostring -local type = type -local setmetatable = setmetatable -local io = io -local math = math -local table_sort = table.sort -local math_max = math.max -local string_char = string.char - -local DEBUG = false - - -local band, lshift, rshift -band = bit32.band -lshift = bit32.lshift -rshift = bit32.rshift - - -local function warn(s) - io.stderr:write(s, '\n') -end - - -local function debug(...) - print('DEBUG', ...) -end - - -local function runtime_error(s, level) - level = level or 1 - error(s, level+1) -end - -local function make_outstate(outbs) - local outstate = {} - outstate.outbs = outbs - outstate.window = {} - outstate.window_pos = 1 - return outstate -end - -local function output(outstate, byte) - -- debug('OUTPUT:', s) - local window_pos = outstate.window_pos - outstate.outbs(byte) - outstate.window[window_pos] = byte - outstate.window_pos = window_pos % 32768 + 1 -- 32K -end - -local function noeof(val, ctx) - return assert(val, 'unexpected end of file with context ' .. tostring(ctx)) -end - -local function hasbit(bits, bit) - return bits % (bit + bit) >= bit -end - -local function memoize(f) - local mt = {} - local t = setmetatable({}, mt) - function mt:__index(k) - local v = f(k) - t[k] = v - return v - end - return t -end - --- small optimization (lookup table for powers of 2) -local pow2 = memoize(function(n) return 2^n end) - ---local tbits = memoize( --- function(bits) --- return memoize( function(bit) return getbit(bits, bit) end ) --- end ) - - --- weak metatable marking objects as bitstream type -local is_bitstream = setmetatable({}, {__mode='k'}) - -local function bytestream_from_file(fh) - local o = {} - function o:read() - local sb = fh:read(1) - if sb then return sb:byte() end - end - return o -end - -local function bytestream_from_string(s) - local i = 1 - local o = {s=s} - function o:read() - local by - if i <= #self.s then - by = self.s:byte(i) - i = i + 1 - end - return by - end - return o -end - -M.stringToBytestream = bytestream_from_string - -local function bytestream_from_function(f) - local i = 0 - local buffer = '' - local o = {} - function o:read() - i = i + 1 - if i > #buffer then - buffer = f() - if not buffer then return end - i = 1 - end - return buffer:byte(i,i) - end - return o -end - -local function bitstream_from_bytestream(bys) - local buf_byte = 0 - local buf_nbit = 0 - local o = {} - - function o:nbits_left_in_byte() - return buf_nbit - end - - function o:read(nbits) - nbits = nbits or 1 - while buf_nbit < nbits do - local byte = bys:read() - if not byte then return end -- note: more calls also return nil - buf_byte = buf_byte + lshift(byte, buf_nbit) - buf_nbit = buf_nbit + 8 - end - local bits - if nbits == 0 then - bits = 0 - elseif nbits == 32 then - bits = buf_byte - buf_byte = 0 - else - bits = band(buf_byte, rshift(0xffffffff, 32 - nbits)) - buf_byte = rshift(buf_byte, nbits) - end - - if nbits == 16 then - -- bugfix: swap bytes - bits = rshift(band(bits, 0xFF00), 8) + lshift(band(bits, 0xFF), 8) - end - - buf_nbit = buf_nbit - nbits - return bits - end - - is_bitstream[o] = true - - return o -end - - -function M.adler32(byte, crc) - local s1 = crc % 65536 - local s2 = (crc - s1) / 65536 - s1 = (s1 + byte) % 65521 - s2 = (s2 + s1) % 65521 - return s2*65536 + s1 -end - -local function get_bitstream(o) - local bs - if is_bitstream[o] then - return o - elseif io.type(o) == 'file' then - bs = bitstream_from_bytestream(bytestream_from_file(o)) - elseif type(o) == 'string' then - bs = bitstream_from_bytestream(bytestream_from_string(o)) - elseif type(o) == 'function' then - bs = bitstream_from_bytestream(bytestream_from_function(o)) - else - runtime_error 'unrecognized type' - end - return bs -end - - -local function get_obytestream(o) - local bs - if io.type(o) == 'file' then - bs = function(sbyte) o:write(string_char(sbyte)) end - elseif type(o) == 'function' then - bs = o - elseif type(o) == 'table' then - -- assume __callable table - bs = o - else - runtime_error('unrecognized type: ' .. tostring(o)) - end - return bs -end - - -local function HuffmanTable(init, is_full) - local t = {} - if is_full then - for val,nbits in pairs(init) do - if nbits ~= 0 then - t[#t+1] = {val=val, nbits=nbits} - --debug('*',val,nbits) - end - end - else - for i=1,#init-2,2 do - local firstval, nbits, nextval = init[i], init[i+1], init[i+2] - --debug(val, nextval, nbits) - if nbits ~= 0 then - for val=firstval,nextval-1 do - t[#t+1] = {val=val, nbits=nbits} - end - end - end - end - table_sort(t, function(a,b) - return a.nbits == b.nbits and a.val < b.val or a.nbits < b.nbits - end) - - -- assign codes - local code = 1 -- leading 1 marker - local nbits = 0 - for i,s in ipairs(t) do - if s.nbits ~= nbits then - code = code * pow2[s.nbits - nbits] - nbits = s.nbits - end - s.code = code - --debug('huffman code:', i, s.nbits, s.val, code, bits_tostring(code)) - code = code + 1 - end - - local minbits = math.huge - local look = {} - for i,s in ipairs(t) do - minbits = math.min(minbits, s.nbits) - look[s.code] = s.val - end - - --for _,o in ipairs(t) do - -- debug(':', o.nbits, o.val) - --end - - -- function t:lookup(bits) return look[bits] end - - local msb = function(bits, nbits) - local res = 0 - for i=1,nbits do - res = lshift(res, 1) + band(bits, 1) - bits = rshift(bits, 1) - end - return res - end - - local tfirstcode = memoize(function(bits) - return pow2[minbits] + msb(bits, minbits) - end) - - function t:read(bs) - local code = 1 -- leading 1 marker - local nbits = 0 - while 1 do - if nbits == 0 then -- small optimization (optional) - code = tfirstcode[noeof(bs:read(minbits), 1)] - nbits = nbits + minbits - else - local b = noeof(bs:read(), 2) - nbits = nbits + 1 - code = lshift(code, 1) + b -- MSB first - end - --debug('code?', code, bits_tostring(code)) - local val = look[code] - if val then - --debug('FOUND', val) - return val - end - end - end - - return t -end - - -local function parse_gzip_header(bs) - -- local FLG_FTEXT = 2^0 - local FLG_FHCRC = 2^1 - local FLG_FEXTRA = 2^2 - local FLG_FNAME = 2^3 - local FLG_FCOMMENT = 2^4 - - local id1 = bs:read(8) - local id2 = bs:read(8) - if id1 ~= 31 or id2 ~= 139 then - runtime_error 'not in gzip format' - end - local cm = bs:read(8) -- compression method - local flg = bs:read(8) -- FLaGs - local mtime = bs:read(32) -- Modification TIME - local xfl = bs:read(8) -- eXtra FLags - local os = bs:read(8) -- Operating System - - if DEBUG then - debug("CM=", cm) - debug("FLG=", flg) - debug("MTIME=", mtime) - -- debug("MTIME_str=",os.date("%Y-%m-%d %H:%M:%S",mtime)) -- non-portable - debug("XFL=", xfl) - debug("OS=", os) - end - - if not os then runtime_error 'invalid header' end - - if hasbit(flg, FLG_FEXTRA) then - local xlen = bs:read(16) - local extra = 0 - for i=1,xlen do - extra = bs:read(8) - end - if not extra then runtime_error 'invalid header' end - end - - local function parse_zstring(bs) - repeat - local by = bs:read(8) - if not by then runtime_error 'invalid header' end - until by == 0 - end - - if hasbit(flg, FLG_FNAME) then - parse_zstring(bs) - end - - if hasbit(flg, FLG_FCOMMENT) then - parse_zstring(bs) - end - - if hasbit(flg, FLG_FHCRC) then - local crc16 = bs:read(16) - if not crc16 then runtime_error 'invalid header' end - -- IMPROVE: check CRC. where is an example .gz file that - -- has this set? - if DEBUG then - debug("CRC16=", crc16) - end - end -end - -local function parse_zlib_header(bs) - local cm = bs:read(4) -- Compression Method - local cinfo = bs:read(4) -- Compression info - local fcheck = bs:read(5) -- FLaGs: FCHECK (check bits for CMF and FLG) - local fdict = bs:read(1) -- FLaGs: FDICT (present dictionary) - local flevel = bs:read(2) -- FLaGs: FLEVEL (compression level) - local cmf = cinfo * 16 + cm -- CMF (Compresion Method and flags) - local flg = fcheck + fdict * 32 + flevel * 64 -- FLaGs - - if cm ~= 8 then -- not "deflate" - runtime_error("unrecognized zlib compression method: " + cm) - end - if cinfo > 7 then - runtime_error("invalid zlib window size: cinfo=" + cinfo) - end - local window_size = 2^(cinfo + 8) - - if (cmf*256 + flg) % 31 ~= 0 then - runtime_error("invalid zlib header (bad fcheck sum) - overflow by " .. ((cmf*256 + flg) % 31)) - end - - if fdict == 1 then - runtime_error("FIX:TODO - FDICT not currently implemented") - local dictid_ = bs:read(32) - end - - return window_size -end - -local function parse_huffmantables(bs) - local hlit = bs:read(5) -- # of literal/length codes - 257 - local hdist = bs:read(5) -- # of distance codes - 1 - local hclen = noeof(bs:read(4), 3) -- # of code length codes - 4 - - local ncodelen_codes = hclen + 4 - local codelen_init = {} - local codelen_vals = { - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} - for i=1,ncodelen_codes do - local nbits = bs:read(3) - local val = codelen_vals[i] - codelen_init[val] = nbits - end - local codelentable = HuffmanTable(codelen_init, true) - - local function decode(ncodes) - local init = {} - local nbits - local val = 0 - while val < ncodes do - local codelen = codelentable:read(bs) - --FIX:check nil? - local nrepeat - if codelen <= 15 then - nrepeat = 1 - nbits = codelen - --debug('w', nbits) - elseif codelen == 16 then - nrepeat = 3 + noeof(bs:read(2), 4) - -- nbits unchanged - elseif codelen == 17 then - nrepeat = 3 + noeof(bs:read(3), 5) - nbits = 0 - elseif codelen == 18 then - nrepeat = 11 + noeof(bs:read(7), 6) - nbits = 0 - else - error 'ASSERT' - end - for i=1,nrepeat do - init[val] = nbits - val = val + 1 - end - end - local huffmantable = HuffmanTable(init, true) - return huffmantable - end - - local nlit_codes = hlit + 257 - local ndist_codes = hdist + 1 - - local littable = decode(nlit_codes) - local disttable = decode(ndist_codes) - - return littable, disttable -end - - -local tdecode_len_base -local tdecode_len_nextrabits -local tdecode_dist_base -local tdecode_dist_nextrabits -local function parse_compressed_item(bs, outstate, littable, disttable) - local val = littable:read(bs) - --debug(val, val < 256 and string_char(val)) - if val < 256 then -- literal - output(outstate, val) - elseif val == 256 then -- end of block - return true - else - if not tdecode_len_base then - local t = {[257]=3} - local skip = 1 - for i=258,285,4 do - for j=i,i+3 do t[j] = t[j-1] + skip end - if i ~= 258 then skip = skip * 2 end - end - t[285] = 258 - tdecode_len_base = t - --for i=257,285 do debug('T1',i,t[i]) end - end - if not tdecode_len_nextrabits then - local t = {} - for i=257,285 do - local j = math_max(i - 261, 0) - t[i] = rshift(j, 2) - end - t[285] = 0 - tdecode_len_nextrabits = t - --for i=257,285 do debug('T2',i,t[i]) end - end - local len_base = tdecode_len_base[val] - local nextrabits = tdecode_len_nextrabits[val] - local extrabits = bs:read(nextrabits) - local len = len_base + extrabits - - if not tdecode_dist_base then - local t = {[0]=1} - local skip = 1 - for i=1,29,2 do - for j=i,i+1 do t[j] = t[j-1] + skip end - if i ~= 1 then skip = skip * 2 end - end - tdecode_dist_base = t - --for i=0,29 do debug('T3',i,t[i]) end - end - if not tdecode_dist_nextrabits then - local t = {} - for i=0,29 do - local j = math_max(i - 2, 0) - t[i] = rshift(j, 1) - end - tdecode_dist_nextrabits = t - --for i=0,29 do debug('T4',i,t[i]) end - end - local dist_val = disttable:read(bs) - local dist_base = tdecode_dist_base[dist_val] - local dist_nextrabits = tdecode_dist_nextrabits[dist_val] - local dist_extrabits = bs:read(dist_nextrabits) - local dist = dist_base + dist_extrabits - - --debug('BACK', len, dist) - for i=1,len do - local pos = (outstate.window_pos - 1 - dist) % 32768 + 1 -- 32K - output(outstate, assert(outstate.window[pos], 'invalid distance')) - end - end - return false -end - - -local function parse_block(bs, outstate) - local bfinal = bs:read(1) - local btype = bs:read(2) - - local BTYPE_NO_COMPRESSION = 0 - local BTYPE_FIXED_HUFFMAN = 1 - local BTYPE_DYNAMIC_HUFFMAN = 2 - local BTYPE_RESERVED_ = 3 - - if DEBUG then - debug('bfinal=', bfinal) - debug('btype=', btype) - end - - if btype == BTYPE_NO_COMPRESSION then - bs:read(bs:nbits_left_in_byte()) - local len = bs:read(16) - local nlen_ = noeof(bs:read(16), 7) - - for i=1,len do - local by = noeof(bs:read(8), 8) - output(outstate, by) - end - elseif btype == BTYPE_FIXED_HUFFMAN or btype == BTYPE_DYNAMIC_HUFFMAN then - local littable, disttable - if btype == BTYPE_DYNAMIC_HUFFMAN then - littable, disttable = parse_huffmantables(bs) - else - littable = HuffmanTable {0,8, 144,9, 256,7, 280,8, 288,nil} - disttable = HuffmanTable {0,5, 32,nil} - end - - repeat - local is_done = parse_compressed_item( - bs, outstate, littable, disttable) - until is_done - else - runtime_error 'unrecognized compression type' - end - - return bfinal ~= 0 -end - - -function M.inflate(t) - local bs = get_bitstream(t.input) - local outbs = get_obytestream(t.output) - local outstate = make_outstate(outbs) - - repeat - local is_final = parse_block(bs, outstate) - until is_final -end -local inflate = M.inflate - - -function M.gunzip(t) - local bs = get_bitstream(t.input) - local outbs = get_obytestream(t.output) - - parse_gzip_header(bs) - - local data_crc32 = 0 - - inflate{input=bs, output=outbs} - - bs:read(bs:nbits_left_in_byte()) - - local expected_crc32 = bs:read(32) - local isize = bs:read(32) -- ignored - --[[ - if DEBUG then - debug('crc32=', expected_crc32) - debug('isize=', isize) - end - ]]-- - if bs:read() then - warn 'trailing garbage ignored' - end -end - - -function M.inflate_zlib(t) - local bs = get_bitstream(t.input) - local outbs = get_obytestream(t.output) - - local window_size_ = parse_zlib_header(bs) - - local data_adler32 = 1 - - inflate{input=bs, output=outbs} - - bs:read(bs:nbits_left_in_byte()) - - - local b3 = bs:read(8) - local b2 = bs:read(8) - local b1 = bs:read(8) - local b0 = bs:read(8) - --[[ - local expected_adler32 = ((b3*256 + b2)*256 + b1)*256 + b0 - if DEBUG then - debug('alder32=', expected_adler32) - end - if not disable_crc then - if data_adler32 ~= expected_adler32 then - runtime_error('invalid compressed data--crc error') - end - end - ]]-- - if bs:read() then - warn 'trailing garbage ignored' - end -end - - -return M - diff --git a/lib/doubleHeight.lua b/lib/doubleHeight.lua deleted file mode 100755 index 74aad3a3..00000000 --- a/lib/doubleHeight.lua +++ /dev/null @@ -1,58 +0,0 @@ - -if not _G.buffer then _G.buffer = require("doubleBuffering") end -local doubleHeight = {} - ------------------------------------------------------------------------------------------------------------------------------------------- - -function doubleHeight.line(x0, y0, x1, y1, color) - local steep = false; - - if math.abs(x0 - x1) < math.abs(y0 - y1 ) then - x0, y0 = swap(x0, y0) - x1, y1 = swap(x1, y1) - steep = true; - end - - if (x0 > x1) then - x0, x1 = swap(x0, x1) - y0, y1 = swap(y0, y1) - end - - local dx = x1 - x0; - local dy = y1 - y0; - local derror2 = math.abs(dy) * 2 - local error2 = 0; - local y = y0; - - for x = x0, x1, 1 do - if steep then - buffer.semiPixelSet(y, x, color); - else - buffer.semiPixelSet(x, y, color) - end - - error2 = error2 + derror2; - - if error2 > dx then - y = y + (y1 > y0 and 1 or -1); - error2 = error2 - dx * 2; - end - end -end - ------------------------------------------------------------------------------------------------------------------------------------------- - --- buffer.clear(0x262626); buffer.draw(true) --- doubleHeight.square(3, 3, 20, 20, 0xFF8888) --- buffer.draw() - ------------------------------------------------------------------------------------------------------------------------------------------- - -return doubleHeight - - - - - - - diff --git a/lib/event.lua b/lib/event.lua deleted file mode 100755 index 6471b06e..00000000 --- a/lib/event.lua +++ /dev/null @@ -1,193 +0,0 @@ - --- This is a fast OpenComputers event processing library written as an alternative --- for its' OpenOS analogue which has become too slow and inefficient in the latest updates - --------------------------------------------------------------------------------------------------------- - -local computer = require("computer") - -local event, handlers, interruptingKeysDown, lastInterrupt = { - interruptingEnabled = true, - interruptingDelay = 1, - interruptingKeyCodes = { - [29] = true, - [46] = true, - [56] = true - }, - push = computer.pushSignal -}, {}, {}, 0 - -local computerPullSignal, computerUptime, mathHuge, mathMin, skipSignalType = computer.pullSignal, computer.uptime, math.huge, math.min - --------------------------------------------------------------------------------------------------------- - -function event.addHandler(callback, signalType, times, interval) - checkArg(1, callback, "function") - checkArg(2, signalType, "string", "nil") - checkArg(3, times, "number", "nil") - checkArg(4, nextTriggerTime, "number", "nil") - - local ID - repeat - ID = math.random(0x7FFFFFFF) - until not handlers[ID] - - handlers[ID] = { - signalType = signalType, - callback = callback, - times = times or mathHuge, - interval = interval, - nextTriggerTime = interval and computerUptime() + interval or 0 - } - - return ID -end - -function event.removeHandler(ID) - checkArg(1, ID, "number") - - if handlers[ID] then - handlers[ID] = nil - - return true - else - return false, "No registered handlers found for ID " .. ID - end -end - -function event.getHandler(ID) - checkArg(1, ID, "number") - - if handlers[ID] then - return handlers[ID] - else - return false, "No registered handlers found for ID " .. ID - end -end - --------------------------------------------------------------------------------------------------------- - -function event.listen(signalType, callback) - checkArg(1, signalType, "string") - checkArg(2, callback, "function") - - for ID, handler in pairs(handlers) do - if handler.callback == callback and handler.signalType == signalType then - return false, "Callback method " .. tostring(callback) .. " is already registered" - end - end - - event.addHandler(callback, signalType) - - return true -end - -function event.ignore(signalType, callback) - checkArg(1, signalType, "string") - checkArg(2, callback, "function") - - for ID, handler in pairs(handlers) do - if handler.signalType == signalType and handler.callback == callback then - handlers[ID] = nil - return true - end - end - - return false, "No registered listeners found for signal \"" .. signalType .. "\" and callback method " .. tostring(callback) -end - --------------------------------------------------------------------------------------------------------- - -function event.timer(interval, callback, times) - checkArg(1, interval, "number") - checkArg(2, callback, "function") - checkArg(3, times, "number", "nil") - - return event.addHandler(callback, nil, times or 1, interval) -end - -event.cancel = event.removeHandler - --------------------------------------------------------------------------------------------------------- - -function event.skip(signalType) - skipSignalType = signalType -end - -function event.pull(arg1, arg2) - local arg1Type, uptime, timeout, preferredTimeout, signalType, signalData = type(arg1), computerUptime() - if arg1Type == "string" then - preferredTimeout, signalType = mathHuge, arg1 - elseif arg1Type == "number" then - preferredTimeout, signalType = arg1, type(arg2) == "string" and arg2 or nil - end - - local deadline = uptime + (preferredTimeout or mathHuge) - repeat - -- Determining pullSignal timeout - timeout = deadline - for ID, handler in pairs(handlers) do - if handler.nextTriggerTime > 0 then - timeout = mathMin(timeout, handler.nextTriggerTime) - end - end - - -- Pulling signal data - signalData = { computerPullSignal(timeout - computerUptime()) } - - -- Handlers processing - for ID, handler in pairs(handlers) do - if handler.times > 0 then - uptime = computerUptime() - - if - (not handler.signalType or handler.signalType == signalData[1]) and - handler.nextTriggerTime <= uptime - then - handler.times = handler.times - 1 - if handler.nextTriggerTime > 0 then - handler.nextTriggerTime = uptime + handler.interval - end - - -- Callback running - pcall(handler.callback, table.unpack(signalData)) - end - else - handlers[ID] = nil - end - end - - -- Program interruption support. It's faster to do it here instead of registering handlers - if signalData[1] == "key_down" or signalData[1] == "key_up" and event.interruptingEnabled then - -- Analysing for which interrupting key is pressed - we don't need keyboard API for this - if event.interruptingKeyCodes[signalData[4]] then - interruptingKeysDown[signalData[4]] = signalData[1] == "key_down" and true or nil - end - - local shouldInterrupt = true - for keyCode in pairs(event.interruptingKeyCodes) do - if not interruptingKeysDown[keyCode] then - shouldInterrupt = false - end - end - - if shouldInterrupt and uptime - lastInterrupt > event.interruptingDelay then - lastInterrupt = uptime - error("interrupted", 0) - end - end - - -- Loop-breaking conditions - if signalData[1] and (not signalType or signalType == signalData[1]) then - if signalData[1] == skipSignalType then - skipSignalType = nil - else - return table.unpack(signalData) - end - end - until uptime >= deadline -end - --------------------------------------------------------------------------------------------------------- - -return event diff --git a/lib/libPNGImage.lua b/lib/libPNGImage.lua deleted file mode 100644 index 44f37c05..00000000 --- a/lib/libPNGImage.lua +++ /dev/null @@ -1,642 +0,0 @@ -local PNGImage = {} - --- --- libPNGimage by TehSomeLuigi --- Revision: -PNGImage.rev = 1 --- --- A library to load, edit and save PNGs for OpenComputers --- - ---[[ - - Feel free to use however you wish. - This header must however be preserved should this be redistributed, even - if in a modified form. - - This software comes with no warranties whatsoever. - - 2014 TehSomeLuigi - -]]-- - - ---local DEFLATE = require("libDEFLATE") -local DeflateLua = require("deflatelua") -local CRC32Lua = require("crc32lua") - -local bit = require("bit32") - -local PNGImagemetatable = {} -PNGImagemetatable.__index = PNGImage - - - - -PNGImage.ColourTypes = { - Greyscale=0, - Truecolour=2, - IndexedColour=3, - GreyscaleAlpha=4, - TruecolourAlpha=6 -} - - - - - - -local band = bit.band -local rshift = bit.rshift - - - --- Unpack 32-bit unsigned integer (most-significant-byte, MSB, first) --- from byte string. -local function __unpack_msb_uint32(s) - local a,b,c,d = s:byte(1,#s) - local num = (((a*256) + b) * 256 + c) * 256 + d - return num -end - -local function __write_msb_uint32(fh, int) - -- MSB B2 B1 LSB - - local msb = rshift(band(0xFF000000, int), 24) - local b2 = rshift(band(0x00FF0000, int), 16) - local b1 = rshift(band(0x0000FF00, int), 8) - local lsb = band(0x000000FF, int) - - fh:write(string.char(msb)) - fh:write(string.char(b2)) - fh:write(string.char(b1)) - fh:write(string.char(lsb)) -end - -local function __wbuf_msb_uint32(buf, int) - -- MSB B2 B1 LSB - - local msb = rshift(band(0xFF000000, int), 24) - local b2 = rshift(band(0x00FF0000, int), 16) - local b1 = rshift(band(0x0000FF00, int), 8) - local lsb = band(0x000000FF, int) - - buf(msb) - buf(b2) - buf(b1) - buf(lsb) -end - -local function __sep_msb_uint32(int) - -- MSB B2 B1 LSB - - local msb = rshift(band(0xFF000000, int), 24) - local b2 = rshift(band(0x00FF0000, int), 16) - local b1 = rshift(band(0x0000FF00, int), 8) - local lsb = band(0x000000FF, int) - - return msb, b2, b1, lsb -end - -local function __pack_msb_uint16(int) - return string.char(rshift(band(int, 0xFF00), 8), band(int, 0xFF)) -end - -local function __pack_lsb_uint16(int) - return string.char(band(int, 0xFF), rshift(band(int, 0xFF00), 8)) -end - --- Read 32-bit unsigned integer (most-significant-byte, MSB, first) from file. -local function __read_msb_uint32(fh) - return __unpack_msb_uint32(fh:read(4)) -end - --- Read unsigned byte (integer) from file -local function __read_byte(fh) - return fh:read(1):byte() -end - - - -local function getBitWidthPerPixel(ihdr) - if ihdr.color_type == PNGImage.ColourTypes.Greyscale then -- Greyscale - return ihdr.bit_depth - end - if ihdr.color_type == PNGImage.ColourTypes.Truecolour then -- Truecolour - return ihdr.bit_depth * 3 - end - if ihdr.color_type == PNGImage.ColourTypes.IndexedColour then -- Indexed-colour - return ihdr.bit_depth - end - if ihdr.color_type == PNGImage.ColourTypes.GreyscaleAlpha then -- Greyscale + Alpha - return ihdr.bit_depth * 2 - end - if ihdr.color_type == PNGImage.ColourTypes.TruecolourAlpha then -- Truecolour + Alpha - return ihdr.bit_depth * 4 - end -end - -local function getByteWidthPerScanline(ihdr) - return math.ceil((ihdr.width * getBitWidthPerPixel(ihdr)) / 8) -end - -local outssmt = {} - -function outssmt:__call(write) - self.str = self.str .. string.char(write) -end - -function outssmt.OutStringStream() - local outss = {str=""} - setmetatable(outss, outssmt) - return outss -end - - - - -local function __parse_IHDR(fh, len) - if len ~= 13 then - error("PNG IHDR Corrupt - should be 13 bytes long") - end - - local ihdr = {} - - ihdr.width = __read_msb_uint32(fh) - ihdr.height = __read_msb_uint32(fh) - ihdr.bit_depth = __read_byte(fh) - ihdr.color_type = __read_byte(fh) - ihdr.compression_method = __read_byte(fh) - ihdr.filter_method = __read_byte(fh) - ihdr.interlace_method = __read_byte(fh) - - --[[ - print("width=", ihdr.width) - print("height=", ihdr.height) - print("bit_depth=", ihdr.bit_depth) - print("color_type=", ihdr.color_type) - print("compression_method=", ihdr.compression_method) - print("filter_method=", ihdr.filter_method) - print("interlace_method=", ihdr.interlace_method) - ]]-- - - return ihdr -end - ---[[ -local function __parse_IDAT(fh, len, commeth) - if commeth ~= 0 then - error("Only zlib/DEFLATE compression supported") - end - - local d, msg = DEFLATE.inflate(fh, len); - - if not d then - return nil, msg - end - - local oh = io.open('dump.dat', 'wb') - oh:write(d.dat) - oh:close() - - return true -end -]]-- - -local function __parse_IDAT(fh, len, commeth, outss) - if commeth ~= 0 then - error("Only zlib/DEFLATE compression supported") - end - - - --local oh = io.open('dump.dat', 'wb') - --oh:write(d.dat) - - --local ph = io.open('pass.dat', 'wb') - - local input = fh:read(len) - - --ph:write(input) - --ph:close() - - local cfg = {input=input, output=outss, disable_crc=true} - - DeflateLua.inflate_zlib(cfg) - - --oh:close() - --- if not d then --- return nil, msg --- end - - return true -end - - - -local function getPNGStdByteAtXY(ihdr, oss, x, y) - local bpsl = getByteWidthPerScanline(ihdr) -- don't include filterType byte -- we don't store that after it has been read - if (x <= 0) or (y <= 0) then - return 0 -- this is what the spec says we should return when the coordinate is out of bounds -- in this part of the code, the coordinates are ONE-BASED like in good Lua - end - local offset_by_y = (y - 1) * bpsl - -- now read it! - local idx = offset_by_y + x - return oss.str:sub(idx, idx):byte() -end - - -local function __paeth_predictor(a, b, c) - local p = a + b - c - local pa = math.abs(p - a) - local pb = math.abs(p - b) - local pc = math.abs(p - c) - if pa <= pb and pa <= pc then - return a - elseif pb <= pc then - return b - else - return c - end -end - - - -local function __parse_IDAT_effective_bytes(outss, ihdr) - local bpsl = getByteWidthPerScanline(ihdr) - local bypsl = math.ceil(getBitWidthPerPixel(ihdr) / 8) - - if outss.str:len() == 0 then - error("Empty string: outss") - end - - local bys = DeflateLua.stringToBytestream(outss.str) - - if not bys then - error("Did not get a bytestream from string", bys, outss) - end - - local out2 = outssmt.OutStringStream() -- __callable table with metatable that stores what you give it - - local y = 0 - - -- x the byte being filtered; - -- a the byte corresponding to x in the pixel immediately before the pixel containing x (or the byte immediately before x, when the bit depth is less than 8); - -- b the byte corresponding to x in the previous scanline; - -- c the byte corresponding to b in the pixel immediately before the pixel containing b (or the byte immediately before b, when the bit depth is less than 8). - - while true do - local filterType = bys:read() - - if filterType == nil then - break - end - - y = y + 1 - - for x = 1, bpsl do - --[..c..][..b..] - --[..a..][..x <--- what is being processed (x) - local a = getPNGStdByteAtXY(ihdr, out2, x - bypsl, y) - local b = getPNGStdByteAtXY(ihdr, out2, x, y - 1) - local c = getPNGStdByteAtXY(ihdr, out2, x - bypsl, y - 1) - - local outVal = 0 - - if filterType == 0 then - -- Recon(x) = Filt(x) - outVal = bys:read() - elseif filterType == 1 then - -- Recon(x) = Filt(x) + Recon(a) - outVal = bys:read() + a - elseif filterType == 2 then - -- Recon(x) = Filt(x) + Recon(b) - outVal = bys:read() + b - elseif filterType == 3 then - -- Recon(x) = Filt(x) + floor((Recon(a) + Recon(b)) / 2) - outVal = bys:read() + math.floor((a + b) / 2) - elseif filterType == 4 then - -- Recon(x) = Filt(x) + PaethPredictor(Recon(a), Recon(b), Recon(c)) - outVal = bys:read() + __paeth_predictor(a, b, c) - else - error("Unsupported Filter Type: " .. tostring(filterType)) - end - - outVal = outVal % 256 - - out2(outVal) - end - end - - return out2 -end - - - -local function __newPNGImage() - local pngi = {} - setmetatable(pngi, PNGImagemetatable) - return pngi -end - -function PNGImage.newFromFile(fn) - local fh = io.open(fn, 'rb') - if not fh then - error("Could not open PNG file") - end - return PNGImage.newFromFileHandle(fh) -end - -function PNGImage.newFromScratch(width, height, bkcol) - local pngi = __newPNGImage() - - local width = tonumber(width) - local height = tonumber(height) - - if (not width) or (width < 1) or (math.floor(width) ~= width) then - error("Invalid param #1 (width) to PNGImage.newFromScratch - integer (>=0) expected") - end - if (not height) or (height < 1) or (math.floor(height) ~= height) then - error("Invalid param #2 (height) to PNGImage.newFromScratch - integer (>=0) expected") - end - - local bkg = {0, 0, 0, 0} -- Transparency - - if type(bkcol) == "table" then - if #bkcol == 3 then - bkg = {bkcol[1], bkcol[2], bkcol[3], 255} -- Opaque colour - elseif #bkcol == 4 then - bkg = {bkcol[1], bkcol[2], bkcol[3], bkcol[4]} -- Defined - else - error("Invalid format for param #3 (bkcol) to PNGImage.newFromScratch: nil or table expected, but the table must be of format {r, g, b} or {r, g, b, a} -- Invalid table") - end - elseif bkcol ~= nil then - error("Invalid format for param #3 (bkcol) to PNGImage.newFromScratch: nil or table expected, but the table must be of format {r, g, b} or {r, g, b, a} -- Parameter is not nil or table") - end - - bkg[1] = tonumber(bkg[1]) - if bkg[1] == nil then - error("PNGImage.newFromScratch: bkg[R] is not numeric") - end - bkg[2] = tonumber(bkg[2]) - if bkg[2] == nil then - error("PNGImage.newFromScratch: bkg[G] is not numeric") - end - bkg[3] = tonumber(bkg[3]) - if bkg[3] == nil then - error("PNGImage.newFromScratch: bkg[B] is not numeric") - end - bkg[4] = tonumber(bkg[4]) - if bkg[4] == nil then - error("PNGImage.newFromScratch: bkg[A] is not numeric") - end - - pngi.ihdr = { - width = width, - height = height, - bit_depth = 8, - color_type = 6, - compression_method = 0, - filter_method = 0, - interlace_method = 0 - } - - -- do this for every pixel.. i.e string.rep(str, w*h) - pngi.data = string.byte(bkg[1], bkg[2], bkg[3], bkg[4]):rep(width * height) - - return pngi -end - -function PNGImage.newFromFileHandle(fh) - local pngi = __newPNGImage() - local expecting = "\137\080\078\071\013\010\026\010" - if fh:read(8) ~= expecting then -- check the 8-byte PNG header exists - error("Not a PNG file") - end - - local ihdr - - local outss = outssmt.OutStringStream() - - while true do - local len = __read_msb_uint32(fh) - local stype = fh:read(4) - - if stype == 'IHDR' then - ihdr, msg = __parse_IHDR(fh, len) - elseif stype == 'IDAT' then - local res, msg = __parse_IDAT(fh, len, ihdr.compression_method, outss) - else - fh:read(len) -- dummy read - end - - local crc = __read_msb_uint32(fh) - - -- print("chunk:", "type=", stype, "len=", len, "crc=", crc) - - if stype == 'IEND' then - break - end - end - - fh:close() - - - if ihdr.filter_method ~= 0 then - error("Unsupported Filter Method: " .. ihdr.filter_method) - end - - if ihdr.interlace_method ~= 0 then - error("Unsupported Interlace Method (Interlacing is currently unsupported): " .. ihdr.interlace_method) - end - - if ihdr.color_type ~= PNGImage.ColourTypes.TruecolourAlpha and ihdr.color_type ~= PNGImage.ColourTypes.Truecolour then - error("Currently, only Truecolour and Truecolour+Alpha images are supported.") - end - - if ihdr.bit_depth ~= 8 then - error("Currently, only images with a bit depth of 8 are supported.") - end - - --[[ - local oh = io.open('before-decode.dat', 'wb') - oh:write(outss.str) - oh:close() - ]]-- - - -- now parse the IDAT chunks - local out2 = __parse_IDAT_effective_bytes(outss, ihdr) - - if ihdr.color_type == PNGImage.ColourTypes.Truecolour then - -- add an alpha layer so it effectively becomes RGBA, not RGB - local inp = out2.str - out2 = outssmt.OutStringStream() - - for i=1, ihdr.width*ihdr.height do - local b = ((i - 1)*3) + 1 - out2(inp:byte(b)) -- R - out2(inp:byte(b + 1)) -- G - out2(inp:byte(b + 2)) -- B - out2(255) -- A - end - end - - pngi.ihdr = ihdr - pngi.data = out2.str - - --[[ - local oh = io.open('effective.dat', 'wb') - oh:write(out2.str) - oh:close() - ]]-- - - return pngi -end - - --- Warning: Co-ordinates are Zero-based but strings are 1-based -function PNGImage:getByteOffsetForPixel(x, y) - return (((y * self.ihdr.width) + x) * 4) + 1 -end - -function PNGImage:getPixel(x, y) - local off = self:getByteOffsetForPixel(x, y) - return self.data:byte(off, off + 3) -end - -function PNGImage:setPixel(x, y, col) - local off = self:getByteOffsetForPixel(x, y) - self.data = table.concat({self.data:sub(1, off - 1), string.char(col[1], col[2], col[3], col[4]), self.data:sub(off + 4)}) -end - -function PNGImage:lineXAB(ax, y, bx, col) - for x=ax, bx do - self:setPixel(x, y, col) - end -end - -function PNGImage:lineYAB(x, ay, by, col) - for y=ay, by do - self:setPixel(x, y, col) - end -end - -function PNGImage:lineRectangleAB(ax, ay, bx, by, col) - self:lineXAB(ax, ay, bx, col) - self:lineXAB(ax, by, bx, col) - self:lineYAB(ax, ay, by, col) - self:lineYAB(bx, ay, by, col) -end - -function PNGImage:fillRectangleAB(ax, ay, bx, by, col) - for x=ax, bx do - for y=ay, by do - self:setPixel(x, y, col) - end - end -end - -function PNGImage:saveToFile(fn) - local fh = io.open(fn, 'wb') - if not fh then - error("Could not open for writing: " .. fn) - end - self:saveToFileHandle(fh) - fh:close() -end - -function PNGImage:getSize() - return self.ihdr.width, self.ihdr.height -end - -function PNGImage:generateRawIDATData(outbuf) - for y = 0, self.ihdr.height - 1 do - outbuf(0) -- filter type is 0 (Filt(x) = Orig(x)) - for x = 0, self.ihdr.width - 1 do - local r, g, b, a = self:getPixel(x, y) - outbuf(r) - outbuf(g) - outbuf(b) - outbuf(a) - end - end -end - -local ZLIB_LITERAL_LIMIT = 65535 - -local function __raw_to_literalZLIB(inbuf) - local outstr = string.char(8, 29) -- zlib headers - - while inbuf.str:len() > 0 do - if inbuf.str:len() > ZLIB_LITERAL_LIMIT then - outstr = outstr .. string.char(0) -- LITERAL[00] FINAL[0] - else - outstr = outstr .. string.char(1) -- LITERAL[00] FINAL[1] - end - local min = math.min(ZLIB_LITERAL_LIMIT, inbuf.str:len()) - outstr = table.concat({outstr, __pack_msb_uint16(min), __pack_msb_uint16(bit.bnot(min)), inbuf.str:sub(1, min)}) - inbuf.str = inbuf.str:sub(min + 1) - end - - local adler32 = 1 - - for i = 1, outstr:len() do - adler32 = DeflateLua.adler32(outstr:byte(i), adler32) - end - - outstr = table.concat({outstr, string.char(__sep_msb_uint32(adler32))}) - - return outstr -end - -function PNGImage:saveToFileHandle(fh) - -- basic PNG 'encoder' - -- most likely temporary - local expecting = "\137\080\078\071\013\010\026\010" - fh:write(expecting) - - local outbuf = outssmt.OutStringStream() - - __wbuf_msb_uint32(outbuf, self.ihdr.width) - __wbuf_msb_uint32(outbuf, self.ihdr.height) - - outbuf(8) -- bit depth - outbuf(6) -- colour type - outbuf(0) -- compression method - outbuf(0) -- filter method - outbuf(0) -- interlace method - - __write_msb_uint32(fh, outbuf.str:len()) -- length field - fh:write("IHDR") -- name of chunk - fh:write(outbuf.str) -- chunk data - __write_msb_uint32(fh, CRC32Lua.crc32_string("IHDR" .. outbuf.str)) -- chunk data CRC32 - outbuf.str = "" -- reset buffer - - -- now onto the IDAT - - self:generateRawIDATData(outbuf) - - local zlibstr = __raw_to_literalZLIB(outbuf) - - local tmpstr = "" - while zlibstr:len() > 0 do - local min = math.min(ZLIB_LITERAL_LIMIT, zlibstr:len()) - tmpstr = zlibstr:sub(1, min) - zlibstr = zlibstr:sub(min + 1) - - __write_msb_uint32(fh, tmpstr:len()) -- length field - fh:write("IDAT") -- name of chunk - fh:write(tmpstr) -- chunk data - __write_msb_uint32(fh, CRC32Lua.crc32_string("IDAT" .. tmpstr)) -- chunk data CRC32 - tmpstr = "" -- reset - end - - __write_msb_uint32(fh, 0) -- length field - fh:write("IEND") -- name of chunk - -- fh:write(tmpstr) -- chunk data - __write_msb_uint32(fh, CRC32Lua.crc32_string("IEND")) -- chunk data CRC32 -end - - - -return PNGImage diff --git a/lib/matrix.lua b/lib/matrix.lua deleted file mode 100644 index b4236c2b..00000000 --- a/lib/matrix.lua +++ /dev/null @@ -1,222 +0,0 @@ - -local matrixLibrary = {} - ----------------------------------------------------- Matrix tables creation ---------------------------------------------------------------- - -function matrixLibrary.newIdentityMatrix(size) - local matrix = {} - - for y = 1, size do - matrix[y] = {} - for x = 1, size do - matrix[y][x] = (x == y) and 1 or 0 - end - end - - return matrix -end - -function matrixLibrary.newCofactorMatrix(matrix) - local cofactorMatrix = {} - for y = 1, #matrix do - cofactorMatrix[y] = {} - for x = 1, #matrix[y] do - cofactorMatrix[y][x] = matrixLibrary.getCofactor(matrix, y, x) - end - end - - return cofactorMatrix -end - -function matrixLibrary.newAdjugateMatrix(matrix) - return matrixLibrary.transpose(matrixLibrary.newCofactorMatrix(matrix)) -end - -function matrixLibrary.newFilledMatrix(width, height, value) - local matrix = {} - - for y = 1, height do - matrix[y] = {} - for x = 1, width do - matrix[y][x] = value - end - end - - return matrix -end - -function matrixLibrary.copy(matrix) - local newMatrix = {} - - for y = 1, #matrix do - newMatrix[y] = {} - for x = 1, #matrix[y] do - newMatrix[y][x] = matrix[y][x] - end - end - - return newMatrix -end - -function matrixLibrary.print(matrix) - print("Matrix size: " .. #matrix .. "x" .. #matrix[1]) - for y = 1, #matrix do - for x = 1, #matrix[y] do - io.write(tostring((not matrix[y]) and "nil" or matrix[y][x])) - io.write(' ') - end - print("") - end - - return matrix -end - ----------------------------------------------------- Matrix arithmetic operations ---------------------------------------------------------------- - -function matrixLibrary.multiply(matrix, data) - local dataType = type(data) - if dataType == "table" then - if (#matrix[1] ~= #data) then error("Couldn't multiply matrixes AxB: A[columns] ~= B[rows]") end - - local result = {} - for i = 1, #matrix do - result[i] = {} - for j = 1, #data[1] do - local resultElement = 0 - for k = 1, #matrix[1] do - resultElement = resultElement + (matrix[i][k] * data[k][j]) - end - result[i][j] = resultElement - end - end - - return result - elseif dataType == "number" then - for y = 1, #matrix do - for x = 1, #matrix[y] do - matrix[y][x] = matrix[y][x] * data - end - end - - return matrix - else - error("Unsupported operation data type: " .. tostring(dataType)) - end -end - -function matrixLibrary.divide(matrix, data) - local dataType = type(data) - if dataType == "table" then - error("Matrix by matrix division doesn't supported yet") - elseif dataType == "number" then - for y = 1, #matrix do - for x = 1, #matrix[y] do - matrix[y][x] = matrix[y][x] / data - end - end - - return matrix - else - error("Unsupported operation data type: " .. tostring(dataType)) - end -end - ----------------------------------------------------- Matrix resizing methods ---------------------------------------------------------------- - -function matrixLibrary.addRow(matrix, row) - if (#matrix[1] ~= #row) then error("Insertion row size doesn't match matrix row size: " .. #row .. " vs " .. #matrix[1]) end - - table.insert(matrix, row) - - return matrix -end - -function matrixLibrary.addColumn(matrix, column) - if (#matrix ~= #column ) then error("Insertion column size doesn't match matrix column size: " .. #column .. " vs " .. #matrix) end - - for y = 1, #column do - table.insert(matrix[y], column[y]) - end - - return matrix -end - -function matrixLibrary.removeRow(matrix, row) - if row > #matrix then error("Can't remove row that is bigger then matrix height") end - - table.remove(matrix, row) - - return matrix -end - -function matrixLibrary.removeColumn(matrix, column) - if column > #matrix[1] then error("Can't remove column that is bigger then matrix width") end - - for y = 1, #matrix do - table.remove(matrix[y], column) - end - - return matrix -end - ----------------------------------------------------- Matrix advanced manipulation methods ---------------------------------------------------------------- - -function matrixLibrary.transpose(matrix) - local transposedMatrix = {} - for x = 1, #matrix[1] do - transposedMatrix[x] = {} - for y = 1, #matrix do - transposedMatrix[x][y] = matrix[y][x] - end - end - return transposedMatrix -end - -function matrixLibrary.getMinor(matrix, row, column) - return matrixLibrary.getDeterminant(matrixLibrary.removeColumn(matrixLibrary.removeRow(matrixLibrary.copy(matrix), row), column)) -end - -function matrixLibrary.getCofactor(matrix, row, column) - return (-1) ^ (row + column) * matrixLibrary.getMinor(matrix, row, column) -end - -function matrixLibrary.getDeterminant(matrix) - local matrixSize = #matrix - if matrixSize ~= #matrix[1] then error("Can't find determinant for matrix, row count != column count: " .. #matrix .. "x" .. #matrix[1]) end - - if matrixSize == 1 then - return matrix[1][1] - elseif matrixSize == 2 then - return matrix[1][1] * matrix[2][2] - matrix[1][2] * matrix[2][1] - else - local determinant = 0 - for j = 1, matrixSize do - determinant = determinant + matrixLibrary.getCofactor(matrix, 1, j) * matrix[1][j] - end - - return determinant - end -end - -function matrixLibrary.invert(matrix) - local determinant = matrixLibrary.getDeterminant(matrix) - if determinant == 0 then error("Can't invert matrix with determinant equals 0") end - - return matrixLibrary.divide(matrixLibrary.newAdjugateMatrix(matrix), determinant) -end - ------------------------------------------------------------------------------------------------------------------------- - --- local m = { --- {2, 5, 4}, --- {-5, 5, 6}, --- {1, 3, 7}, --- } --- matrixLibrary.print(m) --- m = matrixLibrary.invert(m) --- matrixLibrary.print(m) - ------------------------------------------------------------------------------------------------------------------------- - -return matrixLibrary - diff --git a/lib/modemConnection.lua b/lib/modemConnection.lua deleted file mode 100644 index c95efeaf..00000000 --- a/lib/modemConnection.lua +++ /dev/null @@ -1,496 +0,0 @@ - -local event = require("event") -local computer = require("computer") -local component = require("component") -local term = require("term") -local serialization = require("serialization") -local unicode = require("unicode") -local ecs, image -local modem = component.modem -local gpu = component.gpu -local modemConnection = {} - ----------------------------------------------------------------------------------------------------------------------------------- - -modemConnection.port = 322 -modemConnection.waitForConnectionAcceptingDelay = 10 -modemConnection.receiveMessagesFromRobots = true -modemConnection.receiveMessagesFromTablets = true -modemConnection.receiveMessagesFromComputers = true - -local infoMessages = { - userTriesToConnectNoGUI = "Пользователь %s желает установить с вами соединение. Разрешить? Y/N", - noModem = "Этой библиотеке требуется сетевая карта для работы", -} - ----------------------------------------------------------------------------------------------------------------------------------- - -local xSize, ySize = gpu.getResolution() -local computerIcon, robotIcon, tabletIcon -if not component.isAvailable("robot") then - image = require("image") - ecs = require("ECSAPI") - computerIcon = image.load("MineOS/System/OS/Icons/Computer.pic") - robotIcon = image.load("MineOS/System/OS/Icons/Robot.pic") - tabletIcon = image.load("MineOS/System/OS/Icons/Tablet.pic") -end - ----------------------------------------------------------------------------------------------------------------------------------- - -local obj = {} -local function newObj(class, name, ...) - obj[class] = obj[class] or {} - obj[class][name] = {...} -end - -local function sendAcceptingMessage(address) - modem.send(address, modemConnection.port, "connectionAccepted", modemConnection.dataToSend) -end - -local function sendDecliningMessage(address) - modem.send(address, modemConnection.port, "connectionDeclined", modemConnection.dataToSend) -end - -local function tryToConnect(address) - modem.send(address, modemConnection.port, "iWantToConnect", modemConnection.dataToSend) -end - -local function acceptingOrDecliningDialog(address, accepted) - local text1, text2 - -- if accepted == true then - -- text1 = "Установлено соединение с пользователем" - -- text2 = "\"" .. ecs.stringLimit("end", address, 18) .. "\"" - -- else - if accepted == false then - text1 = "Пользователь \"" .. ecs.stringLimit("end", address, 12) .. "\" отказался" - text2 = "установить с вами соединение." - elseif accepted == nil then - text1 = "Пользователь \"" .. ecs.stringLimit("end", address, 12) .. "\" не ответил" - text2 = "на ваш запрос" - end - - ecs.universalWindow("auto", "auto", 36, 0x262626, true, - {"EmptyLine"}, - {"CenterText", ecs.colors.orange, "WirelessConnection"}, - {"EmptyLine"}, - {"CenterText", 0xffffff, text1 }, - {"CenterText", 0xffffff, text2 }, - {"EmptyLine"}, - {"Button", {ecs.colors.orange, 0x262626, "OK"}} - ) -end - -local function askForAcceptConnection(address) - if not component.isAvailable("robot") then - local data = ecs.universalWindow("auto", "auto", 36, 0x262626, true, - {"EmptyLine"}, - {"CenterText", ecs.colors.orange, "WirelessConnection"}, - {"EmptyLine"}, - {"CenterText", 0xffffff, "Пользователь \"" .. ecs.stringLimit("end", address, 12) .. "\" желает" }, - {"CenterText", 0xffffff, "установить с вами беспроводное соединение." }, - {"EmptyLine"}, - {"CenterText", 0xffffff, "Разрешить подключение?" }, - {"EmptyLine"}, - {"Button", {ecs.colors.orange, 0x262626, "Да"}, {0x999999, 0xffffff, "Нет"}} - ) - - if data[1] == "Да" then - modemConnection.remoteAddress = address - sendAcceptingMessage(address) - computer.pushSignal("connectionEstabilishedExitFromGUI") - else - sendDecliningMessage(address) - end - else - term.clear() - term.setCursor(1, 1) - print("Пользователь \"" .. string.sub(address, 1, 12) .. "\" желает установить с вами беспроводное соединение. Разрешить подключение? Y/N") - local answer = term.read() - if string.sub(string.lower(answer), 1, 1) == "y" then - modemConnection.remoteAddress = address - sendAcceptingMessage(address) - print(" ") - print("Соединение установлено.") - print(" ") - -- computer.pushSignal("key_down", component.getPrimary("keyboard").address, 13, 28, "ECS") - else - sendDecliningMessage(address) - term.clear() - term.setCursor(1, 1) - end - end -end - -local function infoAboutClient(userData) - - local arguments = { - "auto", "auto", 36, 0x262626, true, {"EmptyLine"}, {"CenterText", ecs.colors.orange, "Информация о пользователе"}, {"EmptyLine"}, - } - - if userData.isRobot then - table.insert(arguments, {"CenterText", 0xffffff, "Тип: робот"}) - elseif userData.isTablet then - table.insert(arguments, {"CenterText", 0xffffff, "Тип: планшет"}) - elseif not userData.isTablet and not userData.isRobot then - table.insert(arguments, {"CenterText", 0xffffff, "Тип: компьютер"}) - end - - table.insert(arguments, {"CenterText", 0xffffff, "Имя: " .. userData.name}) - table.insert(arguments, {"CenterText", 0xffffff, "Адрес: " .. ecs.stringLimit("end", userData.address, 12)}) - table.insert(arguments, {"CenterText", 0xffffff, "Память: " .. userData.ram .. " KB"}) - - if userData.isTablet or userData.isRobot and userData.hasUpgrades then - table.insert(arguments, {"EmptyLine"}) - table.insert(arguments, {"CenterText", ecs.colors.orange, "Улучшения"}) - table.insert(arguments, {"EmptyLine"}) - if userData.inventoryController then - table.insert(arguments, {"CenterText", 0xffffff, "Контроллер инвентаря"}) - end - if userData.tankController then - table.insert(arguments, {"CenterText", 0xffffff, "Контроллер бака"}) - end - if userData.crafting then - table.insert(arguments, {"CenterText", 0xffffff, "Крафтинг"}) - end - if userData.redstone then - table.insert(arguments, {"CenterText", 0xffffff, "Редстоун-плата"}) - end - if userData.navigation then - table.insert(arguments, {"CenterText", 0xffffff, "Навигация"}) - end - if userData.piston then - table.insert(arguments, {"CenterText", 0xffffff, "Поршень"}) - end - if userData.geolyzer then - table.insert(arguments, {"CenterText", 0xffffff, "Геоанализатор"}) - end - end - - table.insert(arguments, {"EmptyLine"}) - table.insert(arguments, {"Button", {ecs.colors.orange, 0x262626, "OK"}}) - - - ecs.universalWindow(table.unpack(arguments)) -end - -local function modemMessageHandler(_, localAddress, remoteAddress, port, distance, ...) - local messages = {...} - if #messages > 0 then - if port == modemConnection.port then - if messages[1] == "iWantToConnect" and not modemConnection.remoteAddress then - askForAcceptConnection(remoteAddress) - elseif messages[1] == "iAmHereAddMePlease" and not modemConnection.remoteAddress then - if not modemConnection.availableUsers[remoteAddress] then - local userData = serialization.unserialize(messages[2]) - if - (not userData.isRobot and not userData.isTablet and modemConnection.receiveMessagesFromComputers) - or - (userData.isRobot and modemConnection.receiveMessagesFromRobots) - or - (userData.isTablet and modemConnection.receiveMessagesFromTablets) - then - modemConnection.availableUsers[userData.address] = userData - modem.send(remoteAddress, modemConnection.port, "iAmHereAddMePlease", modemConnection.dataToSend) - computer.pushSignal("userlistChanged") - end - end - elseif messages[1] == "iAmDisconnecting" then - if modemConnection.availableUsers[remoteAddress] then - modemConnection.availableUsers[remoteAddress] = nil - computer.pushSignal("userlistChanged") - end - end - end - end -end - -local function createSendingArray() - modemConnection.dataToSend = {} - modemConnection.dataToSend.address = modemConnection.localAddress - modemConnection.dataToSend.name = component.filesystem.getLabel() - modemConnection.dataToSend.ram = math.floor(computer.totalMemory() / 1024) - - if component.isAvailable("robot") then - modemConnection.dataToSend.isRobot = true - if component.isAvailable("inventory_controller") then - modemConnection.dataToSend.inventoryController = true; modemConnection.dataToSend.hasUpgrades = true - end - - if component.isAvailable("tank_controller") then - modemConnection.dataToSend.tankController = true; modemConnection.dataToSend.hasUpgrades = true - end - - if component.isAvailable("crafting") then - modemConnection.dataToSend.crafting = true; modemConnection.dataToSend.hasUpgrades = true - end - - if component.isAvailable("redstone") then - modemConnection.dataToSend.redstone = true; modemConnection.dataToSend.hasUpgrades = true - end - end - - if component.isAvailable("tablet") then - modemConnection.dataToSend.isTablet = true - if component.isAvailable("navigation") then - modemConnection.dataToSend.navigation = true; modemConnection.dataToSend.hasUpgrades = true - end - - if component.isAvailable("piston") then - modemConnection.dataToSend.piston = true; modemConnection.dataToSend.hasUpgrades = true - end - end - - if component.isAvailable("geolyzer") then - modemConnection.dataToSend.geolyzer = true; modemConnection.dataToSend.hasUpgrades = true - end - - modemConnection.dataToSend = serialization.serialize(modemConnection.dataToSend) -end - ---Нарисовать окружность, алгоритм спизжен с вики -local function circle(xCenter, yCenter, radius, color) - gpu.setBackground(color) - local function insertPoints(x, y) - gpu.set(xCenter + x * 2, yCenter + y, " ") - gpu.set(xCenter + x * 2, yCenter - y, " ") - gpu.set(xCenter - x * 2, yCenter + y, " ") - gpu.set(xCenter - x * 2, yCenter - y, " ") - - gpu.set(xCenter + x * 2 + 1, yCenter + y, " ") - gpu.set(xCenter + x * 2 + 1, yCenter - y, " ") - gpu.set(xCenter - x * 2 + 1, yCenter + y, " ") - gpu.set(xCenter - x * 2 + 1, yCenter - y, " ") - end - - local x = 0 - local y = radius - local delta = 3 - 2 * radius; - while (x < y) do - insertPoints(x, y); - insertPoints(y, x); - if (delta < 0) then - delta = delta + (4 * x + 6) - else - delta = delta + (4 * (x - y) + 10) - y = y - 1 - end - x = x + 1 - end - - if x == y then insertPoints(x, y) end -end - -local function drawCircles(xCircle, yCircle, minumumRadius, maximumRadius, step, currentRadius) - for radius = minumumRadius, maximumRadius, step do - if radius == currentRadius then - circle(xCircle, yCircle, radius, 0x888888) - else - circle(xCircle, yCircle, radius, 0xDDDDDD) - end - end -end - -local function drawIconAndAddress(x, y, background, foreground, userData) - if userData.isRobot then - image.draw(x + 3, y, robotIcon) - elseif userData.isTablet then - image.draw(x + 3, y, tabletIcon) - else - image.draw(x + 3, y, computerIcon) - end - - ecs.colorTextWithBack(x, y + 5, foreground, background, ecs.stringLimit("end", userData.address, 14)) - - return x, y, x + 13, y + 5 -end - -local function drawHorizontalIcons() - local height = 8 - local y = math.floor(ySize / 2 - height / 2) - local background = 0x66A8FF - ecs.square(1, y, xSize, height, background) - - local iconWidth = 14 - local spaceBetween = 2 - local totalWidth = ecs.getArraySize(modemConnection.availableUsers) * (iconWidth + spaceBetween) - spaceBetween - local x = math.floor(xSize / 2 - totalWidth / 2) + 1 - - obj.Users = {} - - local counter = 0 - local limit = math.floor(xSize / (iconWidth + spaceBetween)) - y = y + 1 - for address in pairs(modemConnection.availableUsers) do - if counter < limit then - newObj("Users", address, drawIconAndAddress(x, y, background, 0xFFFFFF, modemConnection.availableUsers[address])) - end - x = x + iconWidth + spaceBetween - counter = counter + 1 - end -end - -local function drawSelectedIcon(x, y, background, foreground, userData) - local selectionWidth = 16 - local skokaOtnat = (selectionWidth - 14) / 2 - local oldPixels = ecs.rememberOldPixels(x - skokaOtnat, y, x + selectionWidth - 2, y + 13) - ecs.square(x - skokaOtnat, y, selectionWidth, 8, background) - drawIconAndAddress(x, y + 1, background, foreground, userData) - obj.CykaKnopkaInfo = { ecs.drawButton(x - skokaOtnat, y + 8, selectionWidth, 3, "Информация", 0xff6699, 0xFFFFFF) } - obj.CykaKnopkaConnect = { ecs.drawButton(x - skokaOtnat, y + 11, selectionWidth, 3, "Подключиться", 0xff3333, 0xFFFFFF) } - obj.CykaKnopkaConnect.address = userData.address - return oldPixels -end - -local function connectionGUI() - ecs.square(1, 1, xSize, ySize, 0xEEEEEE) - - local xCircle, yCircle = math.floor(xSize / 2), ySize - 3 - local minumumRadius, maximumRadius = 7, xCircle * 0.8 - local step = 4 - local currentRadius = minumumRadius - local unserializedDataToSend = serialization.unserialize(modemConnection.dataToSend) - - drawIconAndAddress(xCircle - 6, ySize - 6, 0xEEEEEE, 0x262626, unserializedDataToSend) - - while true do - if ecs.getArraySize(modemConnection.availableUsers) > 0 then - currentRadius = 0 - drawCircles(xCircle, yCircle, minumumRadius, maximumRadius, step, currentRadius) - - drawHorizontalIcons() - - local oldPixels, needToUpdate - while true do - if not oldPixels and needToUpdate then - if ecs.getArraySize(modemConnection.availableUsers) <= 0 then - ecs.square(1, 1, xSize, ySize, 0xEEEEEE) - drawIconAndAddress(xCircle - 6, ySize - 6, 0xEEEEEE, 0x262626, unserializedDataToSend) - currentRadius = minumumRadius - break - else - drawHorizontalIcons() - needToUpdate = false - end - end - - local e = { event.pull() } - if e[1] == "touch" then - - if obj.CykaKnopkaInfo and obj.CykaKnopkaConnect then - if ecs.clickedAtArea(e[3], e[4], obj.CykaKnopkaInfo[1], obj.CykaKnopkaInfo[2], obj.CykaKnopkaInfo[3], obj.CykaKnopkaInfo[4]) then - ecs.drawButton(obj.CykaKnopkaInfo[1], obj.CykaKnopkaInfo[2], 16, 3, "Информация", 0x262626, 0xFFFFFF) - os.sleep(0.2) - if oldPixels then ecs.drawOldPixels(oldPixels); oldPixels = nil end - infoAboutClient(modemConnection.availableUsers[obj.CykaKnopkaConnect.address]) - - elseif ecs.clickedAtArea(e[3], e[4], obj.CykaKnopkaConnect[1], obj.CykaKnopkaConnect[2], obj.CykaKnopkaConnect[3], obj.CykaKnopkaConnect[4]) then - ecs.drawButton(obj.CykaKnopkaConnect[1], obj.CykaKnopkaConnect[2], 16, 3, "Подключиться", 0x262626, 0xFFFFFF) - os.sleep(0.2) - if oldPixels then ecs.drawOldPixels(oldPixels); oldPixels = nil end - - local oldInfoPixels = ecs.info("auto", "auto", "", "Ожидание ответа от пользователя...") - tryToConnect(obj.CykaKnopkaConnect.address) - - local function filter(name, _, remoteAddress) - if name == "modem_message" and remoteAddress == obj.CykaKnopkaConnect.address then - return true - end - end - - local e2 = { event.pullFiltered(modemConnection.waitForConnectionAcceptingDelay, filter) } - ecs.drawOldPixels(oldInfoPixels) - - if e2[6] == "connectionAccepted" then - modemConnection.remoteAddress = e2[3] - -- acceptingOrDecliningDialog(obj.CykaKnopkaConnect.address, true) - computer.pushSignal("connectionEstabilishedExitFromGUI") - elseif e2[6] == "connectionDeclined" then - acceptingOrDecliningDialog(obj.CykaKnopkaConnect.address, false) - else - acceptingOrDecliningDialog(obj.CykaKnopkaConnect.address, nil) - end - end - - obj.CykaKnopkaInfo, obj.CykaKnopkaConnect = nil, nil - end - - if oldPixels then ecs.drawOldPixels(oldPixels); oldPixels = nil end - - for address in pairs(obj.Users) do - if ecs.clickedAtArea(e[3], e[4], obj.Users[address][1], obj.Users[address][2], obj.Users[address][3], obj.Users[address][4]) then - oldPixels = drawSelectedIcon(obj.Users[address][1], obj.Users[address][2] - 1, 0xCCCCFF, 0x262626, modemConnection.availableUsers[address]) - break - end - end - elseif e[1] == "userlistChanged" then - needToUpdate = true - elseif e[1] == "connectionEstabilishedExitFromGUI" then - ecs.prepareToExit() - modemConnection.disconnect() - return - end - end - else - drawCircles(xCircle, yCircle, minumumRadius, maximumRadius, step, currentRadius) - currentRadius = currentRadius + step - if currentRadius > (maximumRadius + step) then currentRadius = minumumRadius end - os.sleep(0) - end - end -end - ----------------------------------------------------------------------------------------------------------------------------------- - -function modemConnection.stopReceivingData() - event.ignore("modem_message", modemMessageHandler) -end - -function modemConnection.startReceivingData() - modemConnection.stopReceivingData() - modemConnection.remoteAddress = nil - event.listen("modem_message", modemMessageHandler) -end - -function modemConnection.disconnect() - modem.broadcast(modemConnection.port, "iAmDisconnecting") -end - -function modemConnection.sendPersonalData() - modem.broadcast(modemConnection.port, "iAmHereAddMePlease", modemConnection.dataToSend) -end - -function modemConnection.changePort(newPort) - modem.close(modemConnection.port) - modem.open(newPort) - modemConnection.port = newPort - modemConnection.remoteAddress = nil - modemConnection.availableUsers = {} - modemConnection.localAddress = component.getPrimary("modem").address - createSendingArray() -end - -function modemConnection.search() - modemConnection.availableUsers = {} - modemConnection.remoteAddress = nil - modemConnection.disconnect() - modemConnection.sendPersonalData() - connectionGUI() -end - -function modemConnection.init() - if component.isAvailable("modem") then - modemConnection.changePort(modemConnection.port) - else - ecs.error(infoMessages.noModem) - return - end -end - ----------------------------------------------------------------------------------------------------------------------------------- - -modemConnection.init() - ----------------------------------------------------------------------------------------------------------------------------------- - -return modemConnection - diff --git a/lib/multiScreen.lua b/lib/multiScreen.lua deleted file mode 100644 index 6e76c82e..00000000 --- a/lib/multiScreen.lua +++ /dev/null @@ -1,294 +0,0 @@ -local ecs = require("ECSAPI") -local components = require("component") -local serialization = require("serialization") -local fs = require("filesystem") -local event = require("event") -local unicode = require("unicode") -local image = require("image") - --------------------------------------------------------------------------------------------------------------------------------------------- - -local pathToMultiScreenFolder = "MineOS/System/MultiScreen/" -local pathToConfigFile = pathToMultiScreenFolder .. "Config.cfg" - -local colors = { - background = 0x262626, - foreground = 0xDDDDDD, - currentScreen = ecs.colors.green, - screen = 0xDDDDDD, -} - -local baseResolution = { - width = 135, - height = 50, -} - -local monitors = {} - --------------------------------------------------------------------------------------------------------------------------------------------- - -local function getAllConnectedScreens() - local massiv = {} - for address in pairs(components.list("screen")) do - table.insert(massiv, address) - end - return massiv -end - -local function configurator() - fs.makeDirectory(pathToMultiScreenFolder) - - ecs.setScale(0.7) - - local data = ecs.universalWindow("auto", "auto", 40, 0xeeeeee, true, {"EmptyLine"}, {"CenterText", 0x880000, "Здорово, ебана!"}, {"EmptyLine"}, {"WrappedText", 0x262626, "Добро пожаловать в программу конфигурации мультимонитора. Вам необходимо указать количество мониторов по ширине и высоте, которые вы желаете объединить, а также выбрать желаемый масштаб."}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Ширина"}, {"Input", 0x262626, 0x880000, "Высота"}, {"Slider", 0x262626, 0x880000, 1, 100, 100, "Масштаб: ", "%"}, {"EmptyLine"}, {"Button", {ecs.colors.orange, 0xffffff, "Подтвердить"}, {0x777777, 0xffffff, "Отмена"}}) - local width, height, scale = tonumber(data[1]), tonumber(data[2]), tonumber(data[3]) / 100 - if data[4] == "Отмена" then - ecs.prepareToExit() - print("Калибровка отменена!") - os.exit() - end - - baseResolution.width, baseResolution.height = math.floor(baseResolution.width * scale), math.floor(baseResolution.height * scale) - - -- ecs.error(baseResolution.width .. "x" ..baseResolution.height .. " ccale = " ..scale) - - local countOfConnectedScreens = #getAllConnectedScreens() - - while ((countOfConnectedScreens - 1) ~= width * height) do - data = ecs.universalWindow("auto", "auto", 44, 0xeeeeee, true, {"EmptyLine"}, {"WrappedText", 0x262626, "Теперь вам необходимо подключить внешние мониторы. Вы указали, что собираетесь сделать мультимонитор из " .. width*height .. " мониторов, но на данный момент вы подключили " .. countOfConnectedScreens - 1 .. " мониторов. Так что подключайте все так, как указали, и жмите \"Далее\"."}, {"EmptyLine"}, {"Button", {ecs.colors.orange, 0xffffff, "Далее"}, {0x777777, 0xffffff, "Отмена"}}) - if data[1] == "Отмена" then - ecs.prepareToExit() - print("Калибровка отменена!") - os.exit() - end - countOfConnectedScreens = #getAllConnectedScreens() - end - - ---- - - local w, h = 8, 3 - local xC, yC = 1, 1 - local xSize, ySize = gpu.getResolution() - local mainScreenAddress = gpu.getScreen() - - local function drawMonitors() - ecs.clearScreen(colors.background) - local x, y = 3, 2 - local xPos, yPos = x, y - for j = 1, height do - for i = 1, width do - if j == yC and i == xC then - ecs.square(xPos, yPos, w, h, colors.currentScreen) - else - ecs.square(xPos, yPos, w, h, colors.screen) - end - xPos = xPos + w + 2 - end - yPos = yPos + h + 1 - xPos = x - end - - gpu.setBackground(colors.background) - gpu.setForeground(colors.foreground) - ecs.centerText("x", ySize - 5, "Начинаем процесс калибровки. Коснитесь монитора, подсвеченного зеленым цветом.") - ecs.centerText("x", ySize - 4, "Не нарушайте порядок прокосновений!") - end - - local touchArray = {} - - while xC <= width and yC <= height do - drawMonitors() - local e = {event.pull()} - if e[1] == "touch" then - if e[2] ~= mainScreenAddress then - local success = true - for i = 1, #touchArray do - if touchArray[i] == e[2] then - success = false - break - end - end - if success then - ecs.rebindGPU(e[2]) - gpu.setResolution(baseResolution.width, baseResolution.height) - local color = math.random(0x555555, 0xffffff) - ecs.square(1,1,160,50,color) - gpu.setForeground(0xffffff - color) - ecs.centerText("xy", 0, "Монитор " .. xC .. "x" .. yC .. " откалиброван!") - - -- table.insert(touchArray, {address = e[2], position = {x = xC, y = yC}}) - touchArray[xC] = touchArray[xC] or {} - touchArray[xC][yC] = touchArray[xC][yC] or {} - touchArray[xC][yC].address = e[2] - - ecs.rebindGPU(mainScreenAddress) - ecs.setScale(0.7) - - xC = xC + 1 - if xC > width and yC < height then xC = 1; yC = yC + 1 end - else - ecs.error("Тупая скотина, зачем ты тыкаешь на монитор, которого уже касался? На твое счастье в этой проге есть защита от конченных дебилов вроде тебя.") - end - else - ecs.error("Ну что ты за мудак криворукий! Сказано же, каких мониторов касаться. Не трогай этот монитор.") - end - end - end - - monitors = touchArray - monitors.countOfScreensByWidth = width - monitors.countOfScreensByHeight = height - monitors.screenResolutionByWidth = baseResolution.width - monitors.screenResolutionByHeight = baseResolution.height - monitors.totalResolutionByWidth = baseResolution.width * width - monitors.totalResolutionByHeight = baseResolution.height * height - - ecs.prepareToExit() - ecs.universalWindow("auto", "auto", 40, 0xeeeeee, true, {"EmptyLine"}, {"CenterText", 0x262626, "Калибровка успешно завершена!"}, {"EmptyLine"}, {"Button", {ecs.colors.orange, 0xffffff, "Отлично"}}) - - gpu.setBackground(0x000000) - for x = 1, #monitors do - for y = 1, #monitors[x] do - gpu.bind(monitors[x][y].address) - gpu.fill(1, 1, 160, 50, " ") - end - end - gpu.bind(mainScreenAddress) - ecs.prepareToExit() -end - -local function saveConfig() - local file = io.open(pathToConfigFile, "w") - file:write(serialization.serialize(monitors)) - file:close() -end - -local function loadConfig() - if fs.exists(pathToConfigFile) then - local file = io.open(pathToConfigFile, "r") - monitors = serialization.unserialize(file:read("*a")) - file:close() - print(" ") - print("Файл конфигурации мультимонитора успешно загружен.") - print(" ") - print("Количество экранов: " .. monitors.countOfScreensByWidth .. "x" .. monitors.countOfScreensByHeight .. " шт") - print("Разрешение каждого экрана: " .. monitors.screenResolutionByWidth .. "x" .. monitors.screenResolutionByHeight .. " px") - print("Суммарного разрешение кластера: ".. monitors.totalResolutionByWidth .. "x" .. monitors.totalResolutionByHeight .. " px") - print(" ") - else - configurator() - saveConfig() - loadConfig() - end -end - --------------------------------------------------------------------------------------------------------------------------------------------- - -local currentBackground, currentForeground, currentAddress = 0x000000, 0xffffff, "" - -local multiScreen = {} - -function multiScreen.setBackground(color) - currentBackground = color -end - -function multiScreen.setForeground(color) - currentForeground = color -end - -local function getMonitorAndCoordinates(x, y) - local xMonitor = math.ceil(x / monitors.screenResolutionByWidth) - local yMonitor = math.ceil(y / monitors.screenResolutionByHeight) - local xPos = x - (xMonitor - 1) * monitors.screenResolutionByWidth - local yPos = y - (yMonitor - 1) * monitors.screenResolutionByHeight - - -- print("x = " .. x) - -- print("y = " .. y) - -- print("xMonitor = " .. xMonitor) - -- print("yMonitor = " .. yMonitor) - -- print("xPos = " .. xPos) - -- print("yPos = " .. yPos) - - return xMonitor, yMonitor, xPos, yPos -end - -function multiScreen.clear(color) - for x = 1, #monitors do - for y = 1, #monitors[x] do - gpu.bind(monitors[x][y].address) - gpu.setResolution(monitors.screenResolutionByWidth, monitors.screenResolutionByHeight) - gpu.setBackground(color) - gpu.fill(1, 1, 160, 50, " ") - end - end -end - -function multiScreen.set(x, y, text) - for i = 1, unicode.len(text) do - local xMonitor, yMonitor, xPos, yPos = getMonitorAndCoordinates(x + i - 1, y) - - if currentAddress ~= monitors[xMonitor][yMonitor].address then - gpu.bind(monitors[xMonitor][yMonitor].address) - currentAddress = monitors[xMonitor][yMonitor].address - gpu.setResolution(monitors.screenResolutionByWidth, monitors.screenResolutionByHeight) - end - - if gpu.getBackground ~= currentBackground then gpu.setBackground(currentBackground) end - if gpu.getForeground ~= currentForeground then gpu.setForeground(currentForeground) end - - gpu.set(xPos, yPos, unicode.sub(text, i, i)) - end - -end - -function multiScreen.image(x, y, picture) - local sizeOfPixelData = 4 - - local function convertIndexToCoords(index) - index = (index + sizeOfPixelData - 1) / sizeOfPixelData - local ostatok = index % picture.width - local x = (ostatok == 0) and picture.width or ostatok - local y = math.ceil(index / picture.width) - ostatok = nil - return x, y - end - - local function convertCoordsToIndex(x, y) - return (picture.width * (y - 1) + x) * sizeOfPixelData - sizeOfPixelData + 1 - end - - local xPos, yPos - for i = 1, #picture, sizeOfPixelData do - xPos, yPos = convertIndexToCoords(i) - if picture[i + 2] ~= 0xff then - multiScreen.setBackground(picture[i]) - multiScreen.setForeground(picture[i + 1]) - multiScreen.set(x + xPos - 1, y + yPos - 1, picture[i + 3]) - end - end -end - --------------------------------------------------------------------------------------------------------------------------------------------- - -loadConfig() - -multiScreen.clear(0x000000) - -local picture = image.load("4.png") -multiScreen.image(2, 2, picture) - --- multiScreen.setBackground(ecs.colors.green) --- multiScreen.setForeground(ecs.colors.white) - --- multiScreen.set(130, 2, "Сука мать ебал, пидор ты ебаный, хыыы!") --- multiScreen.set(230, 4, "Сука мать ебал, пидор ты ебаный, хыыы!") - --------------------------------------------------------------------------------------------------------------------------------------------- - -return multiScreen - - - - - - diff --git a/lib/scale.lua b/lib/scale.lua deleted file mode 100755 index 4d1dbd4e..00000000 --- a/lib/scale.lua +++ /dev/null @@ -1,40 +0,0 @@ - --- CopyPizding from eu_tomat *meowderful* guide - -local component = require("component") -local screenScale = {} - ------------------------------------------------------------------------------------------------------- - -function screenScale.getResolution(scale, debug) - if not scale or scale > 1 then - scale = 1 - elseif scale < 0.1 then - scale = 0.1 - end - - local gpu = component.gpu - local sw, sh = component.proxy(gpu.getScreen()).getAspectRatio() - local sa = (sw * 2 - 0.5) / (sh - 0.25) - - local gw, gh = gpu.maxResolution() - if sa > gw / gh then - gh = gw / sa - else - gw = gh * sa - end - - return math.floor(gw * scale), math.floor(gh * scale) -end - -function screenScale.set(scale) - component.gpu.setResolution(screenScale.getResolution(scale)) -end - ------------------------------------------------------------------------------------------------------- - --- screenScale.set(0.8) - ------------------------------------------------------------------------------------------------------- - -return screenScale diff --git a/lib/serialization.lua b/lib/serialization.lua deleted file mode 100755 index 5dc54de4..00000000 --- a/lib/serialization.lua +++ /dev/null @@ -1,20 +0,0 @@ - -require("advancedLua") -local serialization = {} - -------------------------------------------------- Public methods ----------------------------------------------------------------- - -function serialization.serialize(variable, ...) - local variableType = type(variable) - if variableType == "table" then - return table.serialize(variable, ...) - else - return tostring(variable) - end -end - -serialization.unserialize = table.unserialize - ----------------------------------------------------------------------------------------------------------------------- - -return serialization diff --git a/lib/xmlParser.lua b/lib/xmlParser.lua deleted file mode 100644 index a7527eaa..00000000 --- a/lib/xmlParser.lua +++ /dev/null @@ -1,59 +0,0 @@ - -local xml = {} - ------------------------------------------------------------------------------------------------------------- - -function xml.parseargs(s) - local arg = {} - string.gsub(s, "([%-%w]+)=([\"'])(.-)%2", function (w, _, a) - arg[w] = a - end) - return arg -end - -function xml.collect(s) - local stack = {} - local top = {} - table.insert(stack, top) - local ni,c,label,xarg, empty - local i, j = 1, 1 - while true do - ni,j,c,label,xarg, empty = string.find(s, "<(%/?)([%w:]+)(.-)(%/?)>", i) - if not ni then break end - local text = string.sub(s, i, ni-1) - if not string.find(text, "^%s*$") then - table.insert(top, text) - end - if empty == "/" then -- empty element tag - table.insert(top, {label=label, xarg=xml.parseargs(xarg), empty=1}) - elseif c == "" then -- start tag - top = {label=label, xarg=xml.parseargs(xarg)} - table.insert(stack, top) -- new level - else -- end tag - local toclose = table.remove(stack) -- remove top - top = stack[#stack] - if #stack < 1 then - error("nothing to close with "..label) - end - if toclose.label ~= label then - error("trying to close "..toclose.label.." with "..label) - end - table.insert(top, toclose) - end - i = j+1 - end - local text = string.sub(s, i) - if not string.find(text, "^%s*$") then - table.insert(stack[#stack], text) - end - if #stack > 1 then - error("unclosed "..stack[#stack].label) - end - return stack[1] -end - ------------------------------------------------------------------------------------------------------------- - -return xml - -