· 10 years ago · Jun 26, 2016, 04:03 PM
1files = {
2 ".gitattributes",
3 ".gitignore",
4 "startup",
5 ".git/COMMIT_EDITMSG",
6 ".git/config",
7 ".git/description",
8 ".git/FETCH_HEAD",
9 ".git/HEAD",
10 ".git/index",
11 "server/pws.list",
12 "server/users.list",
13 ".git/hooks/README.sample",
14 ".git/info/exclude",
15 ".git/logs/HEAD",
16 "server/maps/editor.lua",
17 "server/maps/editorlayout.nfp",
18 "server/maps/np",
19 ".git/objects/08/ed9449b548cadb7042a357566a9382eb9e9961",
20 ".git/objects/10/7081fe84aa72683e0a970bfad56ca79c1ed2e7",
21 ".git/objects/10/979f14d3360ad35a5899bfea473f013a0fd3af",
22 ".git/objects/1f/40b39d63a394d16ce02d44fc648ca84548789f",
23 ".git/objects/24/267ae62196eb4fa748f71aaa2058fe28a11ba7",
24 ".git/objects/27/425389ece31ed80455305a8c74ca9fcfbb4b9a",
25 ".git/objects/2a/7495002dd070df6eb9efa0e81d05c105fa433f",
26 ".git/objects/3d/b53ac57b15711f1f6a4054203a234078587bf0",
27 ".git/objects/3f/9d125f615d447e0b4563c0499d471a455695af",
28 ".git/objects/45/d850733aefc2197970456175af09ae791e5b56",
29 ".git/objects/4b/d2c0bf7fe8bc426074eefb0ad1371d5a9fb553",
30 ".git/objects/4e/007610d905bd04d2779c5eed42baf4882da8d9",
31 ".git/objects/54/0b7adfe47a1616b977df71f144260b43249850",
32 ".git/objects/5c/b10a7d4270115a28e7d37d0a544f2106a00240",
33 ".git/objects/61/a9c8746e3270c02e6fbed175e0b44e704b7ad3",
34 ".git/objects/70/c1a15cb0c8d9c7ff4f207e39ed9a3e7649fc7d",
35 ".git/objects/7b/bdd7ec946cc99f8090497778ec7c2b52ce4c2d",
36 ".git/objects/82/d3d472fed8e084d15394be50438bd99e303508",
37 ".git/objects/83/d85be963e88257b51f9ca6822238f082265b1f",
38 ".git/objects/95/7ae96e5f76b8de071bddad50987a1fb249c190",
39 ".git/objects/9c/f4c70cb6c2ab03fc9ae88e0f3a47e20de33f28",
40 ".git/objects/b0/365df5dd49db32b9275117b53e2f4035fe8ea4",
41 ".git/objects/bd/b0cabc87cf50106df6e15097dff816c8c3eb34",
42 ".git/objects/c0/9f3f220be1e86eaa761baab6e7153ff8853fd2",
43 ".git/objects/cd/2946ad76b4402e5b3cab9243a9281aad228670",
44 ".git/objects/cf/ae95d30e31855704d863be863db353957355c6",
45 ".git/objects/e6/a616e439c9f0497111e2abfc994333d46d9e43",
46 ".git/refs/heads/master",
47 "server/maps/house1/data.lvl",
48 "server/maps/house1/data.lvlDat",
49 "server/maps/house1/editor",
50 "server/maps/house2/data.lvl",
51 "server/maps/house2/data.lvlDat",
52 "server/maps/house2/editor",
53 "server/saves/Player/data",
54 "server/saves/test/data",
55 ".git/logs/refs/heads/master",
56 ".git/refs/remotes/origin/master",
57 ".git/logs/refs/remotes/origin/master",
58}
59fileData = {
60 "# Auto detect text files and perform LF normalization\
61* text=auto\
62\
63# Custom for Visual Studio\
64*.cs diff=csharp\
65\
66# Standard to msysgit\
67*.doc\009 diff=astextplain\
68*.DOC\009 diff=astextplain\
69*.docx diff=astextplain\
70*.DOCX diff=astextplain\
71*.dot diff=astextplain\
72*.DOT diff=astextplain\
73*.pdf diff=astextplain\
74*.PDF\009 diff=astextplain\
75*.rtf\009 diff=astextplain\
76*.RTF\009 diff=astextplain",
77 "# Windows image file caches\
78Thumbs.db\
79ehthumbs.db\
80\
81# Folder config file\
82Desktop.ini\
83\
84# Recycle Bin used on file shares\
85$RECYCLE.BIN/\
86\
87# Windows Installer files\
88*.cab\
89*.msi\
90*.msm\
91*.msp\
92\
93# Windows shortcuts\
94*.lnk\
95\
96# =========================\
97# Operating System Files\
98# =========================\
99\
100# OSX\
101# =========================\
102\
103.DS_Store\
104.AppleDouble\
105.LSOverride\
106\
107# Thumbnails\
108._*\
109\
110# Files that might appear in the root of a volume\
111.DocumentRevisions-V100\
112.fseventsd\
113.Spotlight-V100\
114.TemporaryItems\
115.Trashes\
116.VolumeIcon.icns\
117\
118# Directories potentially created on remote AFP share\
119.AppleDB\
120.AppleDesktop\
121Network Trash Folder\
122Temporary Items\
123.apdisk",
124 "--test server\
125msgs = {\
126\009\"ping\",\
127\009\"true\",\
128\009\"username\",\
129\009\"exists\",\
130\009\"new\",\
131\009\"wrong pw\",\
132\009\"deleteacc\",\
133\009\"changepw\",\
134\009\"getplayerdata\",\
135\009\"spawnplayer\",\
136\009\"getworlddata\",\
137\009\"uploadplayerdata\"\
138}\
139\
140users = {\
141\009\
142}\
143\
144playerData = {\
145\009users = {\
146\
147\009},\
148\009inventory = {\
149\
150\009},\
151\009worlds = {\
152\
153\009},\
154\009posX = {\
155\
156\009},\
157\009posY = {\
158\
159\009}\
160}\
161z = 0\
162\
163rednet.open(\"top\")\
164\
165while true do\
166\009id, msg, msg2 = rednet.receive()\
167\009if msg == msgs[1] then\
168\009\009print(\"Received ping from \"..tostring(id)..\", sending success.\")\
169\009\009rednet.send(id, msgs[2])\
170\009\009print(\"Success sent.\")\
171\009elseif msg == msgs[3] then\
172\009\009print(\"Received Username: \"..msg2..\" from \"..tostring(id)..\", searching user...\")\
173\009\009local file = fs.open(\"/server/users.list\", \"r\")\
174\009\009local found = false\
175\009\009users = file.readAll()\
176\009\009users = textutils.unserialize(users)\
177\009\009file.close()\
178\009\009for _, user in ipairs(users) do\
179\009\009\009if msg2 == user then\
180\009\009\009\009print(\"User found, sending success.\")\
181\009\009\009\009rednet.send(id, msgs[4])\
182\009\009\009\009found = true\
183\009\009\009\009break\
184\009\009\009else\
185\009\009\009\009found = false\
186\009\009\009end\
187\009\009end\
188\009\009if found == false then\
189\009\009\009print(\"User not found, sending register.\")\
190\009\009\009rednet.send(id, msgs[5])\
191\009\009end\
192\009elseif msg == msgs[5] then\
193\009\009usrData = textutils.unserialize(msg2)\
194\009\009pData = {\
195\009\009\009inventory = {\
196\
197\009\009\009},\
198\009\009\009world = \"house1\",\
199\009\009\009x = 2,\
200\009\009\009y = 2\
201\009\009}\
202\009\009print(\"Register (\"..tostring(id)..\"): \"..usrData.name..\".\")\
203\009\009print(\"Writing to files...\")\
204\009\009local file = fs.open(\"/server/users.list\",\"r\")\
205\009\009users = file.readAll()\
206\009\009users = textutils.unserialize(users)\
207\009\009file.close()\
208\009\009local file = fs.open(\"/server/users.list\",\"w\")\
209\009\009table.insert(users, usrData.name)\
210\009\009file.write(textutils.serialize(users))\
211\009\009file.close()\
212\009\009local file = fs.open(\"/server/pws.list\",\"r\")\
213\009\009pws = file.readAll()\
214\009\009pws = textutils.unserialize(pws)\
215\009\009file.close()\
216\009\009local file = fs.open(\"/server/pws.list\",\"w\")\
217\009\009table.insert(pws, usrData.encpw)\
218\009\009file.write(textutils.serialize(pws))\
219\009\009file.close()\
220\009\009fs.makeDir(\"/server/saves/\"..usrData.name..\"\")\
221\009\009local file = fs.open(\"/server/saves/\"..usrData.name..\"/data\",\"w\")\
222\009\009file.write(textutils.serialize(pData))\
223\009\009file.close()\
224\009\009print(\"Complete, sending success.\")\
225\009\009rednet.send(id, msgs[2])\
226\009elseif msg == msgs[4] then\
227\009\009usrData = textutils.unserialize(msg2)\
228\009\009print(\"Login (\"..tostring(id)..\"): \"..usrData.name..\".\")\
229\009\009print(\"Checking password....\")\
230\009\009local file = fs.open(\"/server/users.list\",\"r\")\
231\009\009users = file.readAll()\
232\009\009users = textutils.unserialize(users)\
233\009\009file.close()\
234\009\009local file = fs.open(\"/server/pws.list\",\"r\")\
235\009\009pws = file.readAll()\
236\009\009pws = textutils.unserialize(pws)\
237\009\009file.close()\
238\009\009local usrnmbr = 0\
239\009\009for _, usr in ipairs(users) do\
240\009\009\009if usr == usrData.name then\
241\009\009\009\009usrnmbr = _\
242\009\009\009\009break\
243\009\009\009end\
244\009\009end\
245\009\009if usrData.encpw == pws[usrnmbr] then\
246\009\009\009print(\"Password correct, sending success.\")\
247\009\009\009rednet.send(id, msgs[2])\
248\009\009else\
249\009\009\009print(\"Password incorrect, sending failure.\")\
250\009\009\009rednet.send(id, msgs[6])\
251\009\009end\
252\009elseif msg == msgs[7] then\
253\009\009usrData = textutils.unserialize(msg2)\
254\009\009print(tostring(id)..\" requested deletion of \"..usrData.name)\
255\009\009print(\"Checking password...\")\
256\009\009local file = fs.open(\"/server/users.list\",\"r\")\
257\009\009users = file.readAll()\
258\009\009users = textutils.unserialize(users)\
259\009\009file.close()\
260\009\009local file = fs.open(\"/server/pws.list\",\"r\")\
261\009\009pws = file.readAll()\
262\009\009pws = textutils.unserialize(pws)\
263\009\009file.close()\
264\009\009local usrnmbr = 0\
265\009\009for _, usr in ipairs(users) do\
266\009\009\009if usr == usrData.name then\
267\009\009\009\009usrnmbr = _\
268\009\009\009\009break\
269\009\009\009end\
270\009\009end\
271\009\009if usrData.encpw == pws[usrnmbr] then\
272\009\009\009fs.delete(\"/server/saves/\"..users[usrnmbr])\
273\009\009\009table.remove(users, usrnmbr)\
274\009\009\009table.remove(pws, usrnmbr)\
275\009\009\009local file = fs.open(\"/server/users.list\",\"w\")\
276\009\009\009file.write(textutils.serialize(users))\
277\009\009\009file.close()\
278\009\009\009local file = fs.open(\"/server/pws.list\",\"w\")\
279\009\009\009file.write(textutils.serialize(pws))\
280\009\009\009file.close()\
281\009\009\009print(\"Password correct and removed account, sending success.\")\
282\009\009\009rednet.send(id, msgs[2])\
283\009\009else\
284\009\009\009print(\"Password invalid, sending failure.\")\
285\009\009\009rednet.send(id, msgs[6])\
286\009\009end\
287\009elseif msg == msgs[8] then\
288\
289\009\009c = textutils.unserialize(msg2)\
290\009\009print(tostring(id)..\" requested changing password of \"..c.oldData.name..\".\")\
291\009\009print(\"Checking password...\")\
292\009\009local file = fs.open(\"/server/users.list\",\"r\")\
293\009\009users = file.readAll()\
294\009\009users = textutils.unserialize(users)\
295\009\009file.close()\
296\009\009local file = fs.open(\"/server/pws.list\",\"r\")\
297\009\009pws = file.readAll()\
298\009\009pws = textutils.unserialize(pws)\
299\009\009file.close()\
300\009\009local usrnmbr = 0\
301\009\009for _, usr in ipairs(users) do\
302\009\009\009if usr == c.newData.name and usr == c.oldData.name then\
303\009\009\009\009usrnmbr = _\
304\009\009\009\009break\
305\009\009\009end\
306\009\009end\
307\009\009if c.oldData.encpw == pws[usrnmbr] then\
308\
309\009\009\009pws[usrnmbr] = c.newData.encpw\
310\009\009\009local file = fs.open(\"/server/pws.list\",\"w\")\
311\009\009\009file.write(textutils.serialize(pws))\
312\009\009\009file.close()\
313\009\009\009print(\"Password correct and changed. Sending success.\")\
314\009\009\009rednet.send(id, msgs[2])\
315\009\009else\
316\009\009\009rednet.send(id, msgs[6])\
317\009\009end\
318\009elseif msg == msgs[10] then\
319\009\009print(tostring(id)..\" requested \"..msg2..\" spawn...\")\
320\009\009local file = fs.open(\"/server/saves/\"..msg2..\"/data\",\"r\")\
321\009\009local pData = file.readAll()\
322\009\009pData = textutils.unserialize(pData)\
323\009\009file.close()\
324\009\009if playerData.users[msg2] ~= nil then\
325\009\009\009--[[table.remove(playerData.users, _)\
326\009\009\009table.remove(playerData.inventory, _)\
327\009\009\009table.remove(playerData.worlds, _)\
328\009\009\009table.remove[playerData.posX, _)\
329\009\009\009table.remove(playerData.posY, _)]]\
330\009\009\009playerData.users[msg2] = nil\
331\009\009\009playerData.inventory[msg2] = nil\
332\009\009\009playerData.worlds[msg2] = nil\
333\009\009\009playerData.posX[msg2] = nil\
334\009\009\009playerData.posY[msg2] = nil\
335\009\009end\
336\009\009--[[table.insert(playerData.users, msg2)\
337\009\009table.insert(playerData.inventory, pData.inventory)\
338\009\009table.insert(playerData.worlds, pData.world)\
339\009\009table.insert(playerData.posX, tostring(pData.x))\
340\009\009table.insert(playerData.posY, tostring(pData.y))]]\
341\009\009playerData.users[msg2] = msg2\
342\009\009playerData.inventory[msg2] = pData.inventory\
343\009\009playerData.worlds[msg2] = pData.world\
344\009\009playerData.posX[msg2] = pData.x\
345\009\009playerData.posY[msg2] = pData.y\
346\009\009print(\"Player inserted into world., sending success and players.\")\
347\009\009rednet.send(id, msgs[2], textutils.serialize(playerData))\
348\009elseif msg == msgs[11] then\
349\009\009mapData = {\
350\009\009\009layout = \"\",\
351\009\009\009data = {\
352\
353\009\009\009}\
354\009\009}\
355\009\009print(tostring(id)..\" requested map \"..msg2..\".\")\
356\009\009local file = fs.open(\"/server/maps/\"..msg2..\"/data.lvl\",\"r\")\
357\009\009mapData.layout = file.readAll()\
358\009\009file.close()\
359\009\009local file = fs.open(\"/server/maps/\"..msg2..\"/data.lvlDat\",\"r\")\
360\009\009local inhalt = file.readAll()\
361\009\009mapData.data = textutils.unserialize(inhalt)\
362\009\009file.close()\
363\009\009rednet.send(id, msgs[2], textutils.serialize(mapData))\
364\009elseif msg == msgs[9] then\
365\009\009--[[z = z+1\
366\009\009print(\"Requested data \"..tostring(z))\
367\009\009for _, usr in ipairs(playerData.users) do\
368\009\009\009print(usr)\
369\009\009\009print(tostring(playerData.posX[_]))\
370\009\009\009print(tostring(playerData.posY[_]))\
371\009\009end]]\
372\009\009rednet.send(id, msgs[2], textutils.serialize(playerData))\
373\009elseif msg == msgs[12] then\
374\009\009local changed = textutils.unserialize(msg2)\
375\009\009print(\"Move \"..changed.changer.name)\
376\009\009\009--print(tostring(playerData.posX[changed.changer.name]))\
377\009\009\009--print(tostring(playerData.posY[changed.changer.name]))\
378\009\009\009\009playerData.posX[changed.changer.name] = changed.playerData.posX[changed.changer.name]\
379\009\009\009\009playerData.posY[changed.changer.name] = changed.playerData.posY[changed.changer.name]\
380\009\009\009\009playerData.inventory[changed.changer.name] = changed.playerData.inventory[changed.changer.name]\
381\009\009\009\009--print(usr)\
382\009\009\009\009local a = tostring(playerData.posX[changed.changer.name])\
383\009\009\009\009local b = tostring(playerData.posY[changed.changer.name])\
384\009\009\009\009local c = tostring(playerData.worlds[changed.changer.name])\
385\009\009print(tostring(id)..\" moved ->\"..a..\" \"..b..\" \"..c)\
386\009\009u = changed.changer.name\
387\009\009local file = fs.open(\"/server/saves/\"..u..\"/data\",\"r\")\
388\009\009local inhalt = file.readAll()\
389\009\009inhalt = textutils.unserialize(inhalt)\
390\009\009file.close()\
391\009\009\
392\009\009inhalt.world = playerData.worlds[changed.changer.name]\
393\009\009inhalt.inventory = playerData.inventory[changed.changer.name]\
394\009\009inhalt.x = playerData.posX[changed.changer.name]\
395\009\009inhalt.y = playerData.posY[changed.changer.name]\
396\009\009local file = fs.open(\"/server/saves/\"..u..\"/data\",\"w\")\
397\009\009file.write(textutils.serialize(inhalt))\
398\009\009file.close()\
399\009end\
400end",
401 "Renamed to startup",
402 "[core]\
403\009bare = false\
404\009filemode = false\
405\009symlinks = false\
406\009ignorecase = true\
407\009logallrefupdates = true\
408[core]\
409\009repositoryformatversion = 0\
410[remote \"origin\"]\
411\009url = https://github.com/Piorjade/ComputerCraft-MMORPG-Server.git\
412[remote \"origin\"]\
413\009fetch = +refs/heads/*:refs/remotes/origin/*\
414[branch \"master\"]\
415\009remote = origin\
416\009merge = refs/heads/master",
417 "Unnamed repository; edit this file 'description' to name the repository.",
418 "107081fe84aa72683e0a970bfad56ca79c1ed2e7\009\009branch 'master' of https://github.com/Piorjade/ComputerCraft-MMORPG-Server",
419 "ref: refs/heads/master",
420 "DIRC\000\000\000\002\000\000\000\016Wo??:+T?Wo??::??\000\000\000\000\000\000\000\000\000\000??\000\000\000\000\000\000\000\000\000\000\001??????P\016m??P???\022???4\000\014.gitattributes\000\000\000\000Wo??:+T?Wo??;\031z?\000\000\000\000\000\000\000\000\000\000??\000\000\000\000\000\000\000\000\000\000\002??)F?v?@.[<??C?(\026?\"?p\000\
421.gitignore\000\000\000\000\000\000\000\000Wo??&???Wo??\027\005\023\000\000\000\000\000\000\000\000\000\000\000??\000\000\000\000\000\000\000\000\000\000\018?\016??\020?6\
422?ZX???G?\001:\015?\000\022server/maps/editor.lua\000\000\000\000Wo??&?e?Wl\029?1???\000\000\000\000\000\000\000\000\000\000??\000\000\000\000\000\000\000\000\000\000\003????\"\011??n?v\027???\021?????\000\028server/maps/editorlayout.nfp\000\000\000\000\000\000Wo??)??Wj??+?'d\000\000\000\000\000\000\000\000\000\000??\000\000\000\000\000\000\000\000\000\000\000?6]??I?2?'Q\023?>/@5???\000\027server/maps/house1/data.lvl\000\000\000\000\000\000\000Wo??*\030t?Wk?\027 m?\000\000\000\000\000\000\000\000\000\000??\000\000\000\000\000\000\000\000\000\000\0006\\?\
423}Bp\017Z(??}\
424TO!\006?\002@\000\030server/maps/house1/data.lvlDat\000\000\000\000Wo??*?M?Wj?\000\028s?\000\000\000\000\000\000\000\000\000\000??\000\000\000\000\000\000\000\000\000\000\025?E?Ps:??\025ypEau?\009?y\030[V\000\025server/maps/house1/editor\000Wo??+1\"?Wj??+?'d\000\000\000\000\000\000\000\000\000\000??\000\000\000\000\000\000\000\000\000\000\000?6]??I?2?'Q\023?>/@5???\000\027server/maps/house2/data.lvl\000\000\000\000\000\000\000Wo??+@b\020Wo?E.??x\000\000\000\000\000\000\000\000\000\000??\000\000\000\000\000\000\000\000\000\000\004:'BS???\030?\004U0Z?t??K?\000\030server/maps/house2/data.lvlDat\000\000\000\000Wo??+??<Wj?\000\028s?\000\000\000\000\000\000\000\000\000\000??\000\000\000\000\000\000\000\000\000\000\025?E?Ps:??\025ypEau?\009?y\030[V\000\025server/maps/house2/editor\000Wo??(]\
425?Wj?\003\027u?(\000\000\000\000\000\000\000\000\000\000??\000\000\000\000\000\000\000\000\000\001??K???\127?B`t??\
426?7\029Z??S\000\014server/maps/np\000\000\000\000Wo??%?\009?Wk??5CA?\000\000\000\000\000\000\000\000\000\000??\000\000\000\000\000\000\000\000\000\000\000??[?c?W?\031???\"8??&[\031\000\015server/pws.list\000\000\000Wo??,4?lWo?r+a?$\000\000\000\000\000\000\000\000\000\000??\000\000\000\000\000\000\000\000\000\000\000<??\018_a]D~\011Ec?I?G\026EV??\000\024server/saves/Player/data\000\000Wo??,??$Wo?4%\"\014?\000\000\000\000\000\000\000\000\000\000??\000\000\000\000\000\000\000\000\000\000\000;?\022?9??Iq\017???C3?m?C\000\022server/saves/test/data\000\000\000\000Wo??&T?\024Wk??5$??\000\000\000\000\000\000\000\000\000\000??\000\000\000\000\000\000\000\000\000\000\000/{???l???Iwx?|+R?L-\000\017server/users.list\000Wo??-\
427+?Wo? q??\000\000\000\000\000\000\000\000\000\000??\000\000\000\000\000\000\000\000\000\000 h?z?n_v??\007\027?P?z\031?I??\000\007startup\000\000\000TREE\000\000\000?\00016 1\
428a??tn2p?.o??u?NpKz?server\00013 2\
429p??\\?????O ~9?>vI?}maps\0009 2\
430\008?I?H??pB?WVj???ahouse1\0003 0\
431$&z?!??O?H?\026? X?(?\027?house2\0003 0\
432???\0141?W\004?c??=?S?sU?saves\0002 2\
433???\012?«\003???\015:G?\
434??(test\0001 0\
435T\011z??z\022\022?w?q?D&\011C$?PPlayer\0001 0\
436???r?????S??PC??05\008k7\011I\006??x>|#;]??b?K?\019",
437 "{\
438 \"Nobody\",\
439 \"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08\",\
440 \"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08\",\
441 \"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08\",\
442}",
443 "{\
444 \"Nobody\",\
445 \"Test\",\
446 \"test\",\
447 \"Player\",\
448}",
449 "#!/bin/sh\
450#\
451# Place appropriately named executable hook scripts into this directory\
452# to intercept various actions that git takes. See `git help hooks` for\
453# more information.",
454 "# File patterns to ignore; see `git help ignore` for more information.\
455# Lines that start with '#' are comments.",
456 "0000000000000000000000000000000000000000 3db53ac57b15711f1f6a4054203a234078587bf0 Piorjade <piorjadelp@gmail.com> 1466956418 +0200\009commit (initial): :boom::camel: Added .gitattributes & .gitignore files\
4573db53ac57b15711f1f6a4054203a234078587bf0 1f40b39d63a394d16ce02d44fc648ca84548789f Piorjade <piorjadelp@gmail.com> 1466956450 +0200\009commit: Added Code to GitHub\
4581f40b39d63a394d16ce02d44fc648ca84548789f 107081fe84aa72683e0a970bfad56ca79c1ed2e7 Piorjade <piorjadelp@gmail.com> 1466956702 +0200\009commit: Renamed to startup",
459 "--[[\
460Map maker for my MMORPG\
461Intended to be used by gameserver-owners!\
462]]\
463\
464--Variablen\
465\
466objects = {\
467\009[\"door\"] = true,\
468\009[\"ladder\"] = true,\
469\009[\"wall\"] = true,\
470\009[\"remove\"] = true\
471}\
472\
473mapData = {\
474\009blocksX = {\
475\
476\009},\
477\009blocksY = {\
478\
479\009},\
480\009laddersX = {\
481\
482\009},\
483\009laddersY = {\
484\
485\009},\
486\009doorsX = {\
487\
488\009},\
489\009doorsY = {\
490\
491\009},\
492\009spawnX = 1,\
493\009spawnY = 1,\
494}\
495\
496selectedObj = \"\"\
497\
498--Funktionen\
499\
500function limitRead(nLimit, replaceChar)\
501 term.setCursorBlink(true)\
502 local cX, cY = term.getCursorPos()\
503 local rString = \"\"\
504 if replaceChar == \"\" then replaceChar = nil end\
505 repeat\
506 local event, p1 = os.pullEventRaw()\
507 if event == \"char\" then\
508 -- Character event\
509 if #rString + 1 <= nLimit then\
510 rString = rString .. p1\
511 write(replaceChar or p1)\
512 end\
513 elseif event == \"key\" and p1 == keys.backspace and #rString >= 1 then\
514 -- Backspace\
515 rString = string.sub(rString, 1, #rString-1)\
516 xPos, yPos = term.getCursorPos()\
517 term.setCursorPos(xPos-1, yPos)\
518 write(\" \")\
519 term.setCursorPos(xPos-1, yPos)\
520 end\
521 until event == \"key\" and p1 == keys.enter\
522 term.setCursorBlink(false)\
523 --print() -- Skip to the next line after clicking enter.\
524 return rString\
525end\
526\
527function clear(bg, fg)\
528\009term.setCursorPos(1,1)\
529\009term.setBackgroundColor(bg)\
530\009term.setTextColor(colors.white)\
531\009term.clear()\
532end\
533\
534function editor()\
535\009clear(colors.black, colors.white)\
536\009editing = true\
537\009oldTerm = term.native()\
538\009paintutils.drawImage(_layout, 1, 1)\
539\009mapWindow = window.create(oldTerm, 1, 1, 25, 9)\
540\009term.redirect(mapWindow)\
541\009paintutils.drawImage(_map, 1, 1)\
542\009maxX = 25\
543\009maxY = 9\
544\009term.redirect(oldTerm)\
545\009searchBox = window.create(oldTerm, 2, 12, 23, 1)\
546\009searchBox.setBackgroundColor(colors.gray)\
547\009searchBox.setTextColor(colors.lime)\
548\009searchBox.clear()\
549\009searchBox.write(\"Enter function...\")\
550\009while true do\
551\009\009local event, button, x, y = os.pullEventRaw()\
552\
553\009\009if event == \"mouse_click\" and button == 1 and x >= 2 and x <= 24 and y == 12 then\
554\009\009\009term.redirect(searchBox)\
555\009\009\009term.setCursorPos(1,1)\
556\009\009\009term.clear()\
557\009\009\009local eingabe = limitRead(23)\
558\009\009\009if #eingabe > 0 then\
559\009\009\009\009for key, a in pairs(objects) do\
560\009\009\009\009\009if eingabe == key then\
561\009\009\009\009\009\009selectedObj = eingabe\
562\009\009\009\009\009\009term.setCursorPos(1,1)\
563\009\009\009\009\009\009term.clear()\
564\009\009\009\009\009\009term.write(selectedObj)\
565\009\009\009\009\009\009break\
566\009\009\009\009\009else\
567\009\009\009\009\009\009term.setCursorPos(1,1)\
568\009\009\009\009\009\009term.clear()\
569\009\009\009\009\009\009term.write(\"Object not found.\")\
570\009\009\009\009\009end\
571\009\009\009\009end\
572\009\009\009end\
573\009\009\009term.redirect(oldTerm)\
574\009\009elseif event == \"mouse_click\" and button == 1 and x >= 1 and x <= 25 and y >= 1 and y <= 9 then\
575\009\009\009if selectedObj ~= \"\" then\
576\009\009\009\009if selectedObj == \"wall\" then\
577\009\009\009\009\009term.setCursorPos(x, y)\
578\009\009\009\009\009term.setBackgroundColor(colors.brown)\
579\009\009\009\009\009term.setTextColor(colors.white)\
580\009\009\009\009\009term.write(\"W\")\
581\009\009\009\009\009table.insert(mapData.blocksX, x)\
582\009\009\009\009\009table.insert(mapData.blocksY, y)\
583\009\009\009\009\009redrawBlocks()\
584\009\009\009\009elseif selectedObj == \"remove\" then\
585\009\009\009\009\009for _, a in ipairs(mapData.blocksX) do\
586\009\009\009\009\009\009if a == x and y == mapData.blocksY[_] then\
587\009\009\009\009\009\009\009table.remove(mapData.blocksX, _)\
588\009\009\009\009\009\009\009table.remove(mapData.blocksY, _)\
589\009\009\009\009\009\009\009paintutils.drawImage(_map, 1, 1)\
590\009\009\009\009\009\009\009redrawBlocks()\
591\009\009\009\009\009\009end\
592\009\009\009\009\009end\
593\009\009\009\009\009for _, a in ipairs(mapData.laddersX) do\
594\009\009\009\009\009\009if a == x and y == mapData.laddersY[_] then\
595\009\009\009\009\009\009\009table.remove(mapData.laddersX, _)\
596\009\009\009\009\009\009\009table.remove(mapData.laddersY, _)\
597\009\009\009\009\009\009\009paintutils.drawImage(_map, 1, 1)\
598\009\009\009\009\009\009\009redrawBlocks()\
599\009\009\009\009\009\009end\
600\009\009\009\009\009end\
601\009\009\009\009elseif selectedObj == \"ladder\" then\
602\009\009\009\009\009term.setCursorPos(x, y)\
603\009\009\009\009\009term.setBackgroundColor(colors.black)\
604\009\009\009\009\009term.setTextColor(colors.white)\
605\009\009\009\009\009term.write(\"L\")\
606\009\009\009\009\009table.insert(mapData.laddersX, x)\
607\009\009\009\009\009table.insert(mapData.laddersY, y)\
608\009\009\009\009\009redrawBlocks()\
609\009\009\009\009end\
610\009\009\009end\
611\009\009elseif event == \"key\" and button == keys.delete then\
612\009\009\009local file = fs.open(fldr..\"data.lvlDat\",\"w\")\
613\009\009\009file.write(textutils.serialize(mapData))\
614\009\009\009file.close()\
615\
616\009\009\009break\
617\009\009end\
618\009end\
619end\
620\
621function redrawBlocks()\
622\009for _, x in ipairs(mapData.blocksX) do\
623\009\009term.setCursorPos(x, mapData.blocksY[_])\
624\009\009term.setBackgroundColor(colors.brown)\
625\009\009term.setTextColor(colors.white)\
626\009\009term.write(\"W\")\
627\009end\
628\009for _, x in ipairs(mapData.laddersX) do\
629\009\009term.setCursorPos(x, mapData.laddersY[_])\
630\009\009term.setBackgroundColor(colors.black)\
631\009\009term.setTextColor(colors.white)\
632\009\009term.write(\"L\")\
633\009end\
634end\
635\
636\
637--Code\
638\
639local args = {...}\
640\
641if #args ~= 2 then\
642\009print(\"Usage: <thisfile> <pathToMapPicture> <editorLayoutDirectory>\")\
643else\
644\009if fs.exists(args[1]) then\
645\009\009fldr = args[1]\
646\009\009_map = paintutils.loadImage(args[1]..\"data.lvl\")\
647\009\009_layout = paintutils.loadImage(args[2])\
648\009\009editor()\
649\009end\
650\009--do stuff\
651end",
652 "bbbbb 7 \
653bbbbbbbbbbbbbbbbbbbbbbbbb7 \
654 7 \
655 7 \
656 7 \
657 7 \
658 7 \
659 7 \
660 7 \
661777777777777777777777777777777777777777777777777777\
662 7 \
663 7 \
664 7 \
665 7 \
666 7 \
667 7 \
668 7 \
669 7 \
670 7 ",
671 "--[[\
672 NPaintPro\
673 By NitrogenFingers\
674]]--\
675 \
676--The screen size\
677local w,h = term.getSize()\
678--Whether or not the program is currently waiting on user input\
679local inMenu = false\
680--Whether or not a drop down menu is active\
681local inDropDown = false\
682--Whether or not animation tools are enabled (use -a to turn them on)\
683local animated = false\
684--Whether or not the text tools are enabled (use -t to turn them on)\
685local textual = false\
686--Whether or not \"blueprint\" display mode is on\
687local blueprint = false\
688--Whether or not the \"layer\" display is on\
689local layerDisplay = false\
690--Whether or not the interface is presently hidden\
691local interfaceHidden = false\
692--Whether or not the \"direction\" display is on\
693local printDirection = false\
694--The tool/mode npaintpro is currently in. Default is \"paint\"\
695--For a list of modes, check out the help file\
696local state = \"paint\"\
697--Whether or not the program is presently running\
698local isRunning = true\
699--The rednet address of the 3D printer, if one has been attached\
700local printer = nil\
701 \
702--The list of every frame, containing every image in the picture/animation\
703--Note: nfp files always have the picture at frame 1\
704local frames = { }\
705--How many frames are currently in the given animation.\
706local frameCount = 1\
707--The Colour Picker column\
708local column = {}\
709--The offset of visible colours in the picker column, if the screen cannot fit all 16\
710local columnoffset = 0\
711--The currently selected left and right colours\
712local lSel,rSel = colours.white,nil\
713--The amount of scrolling on the X and Y axis\
714local sx,sy = 0,0\
715--The alpha channel colour\
716--Change this to change default canvas colour\
717local alphaC = colours.black\
718--The currently selected frame. Default is 1\
719local sFrame = 1\
720--The contents of the image buffer- contains contents, width and height\
721local buffer = nil\
722--The position, width and height of the selection rectangle\
723local selectrect = nil\
724 \
725--Whether or not text tools are enabled for this document\
726local textEnabled = false\
727--The X and Y positions of the text cursor\
728local textCurX, textCurY = 1,1\
729 \
730--The currently calculated required materials\
731local requiredMaterials = {}\
732--Whether or not required materials are being displayed in the pallette\
733local requirementsDisplayed = false\
734--A list of the rednet ID's all in-range printers located\
735local printerList = { }\
736--A list of the names of all in-range printers located. Same as the printerList in reference\
737local printerNames = { }\
738--The selected printer\
739local selectedPrinter = 1\
740--The X,Y,Z and facing of the printer\
741local px,py,pz,pfx,pfz = 0,0,0,0,0\
742--The form of layering used\
743local layering = \"up\"\
744 \
745--The animation state of the selection rectangle and image buffer\
746local rectblink = 0\
747--The ID for the timer\
748local recttimer = nil\
749--The radius of the brush tool\
750local brushsize = 3\
751--Whether or not \"record\" mode is activated (animation mode only)\
752local record = false\
753--The time between each frame when in play mode (animation mode only)\
754local animtime = 0.3\
755 \
756--The current \"cursor position\" in text mode\
757local cursorTexX,cursorTexY = 1,1\
758 \
759--A list of hexidecimal conversions from numbers to hex digits\
760local hexnums = { [10] = \"a\", [11] = \"b\", [12] = \"c\", [13] = \"d\", [14] = \"e\" , [15] = \"f\" }\
761--The NPaintPro logo (divine, isn't it?)\
762local logo = {\
763\"fcc 3 339\";\
764\" fcc 9333 33\";\
765\" fcc 933 333 33\";\
766\" fcc 933 33 33\";\
767\" fcc 933 33 33\";\
768\" c88 333 93333\";\
769\" 888 333 9333\";\
770\" 333 3 333 939\";\
771}\
772--The Layer Up and Layer Forward printing icons\
773local layerUpIcon = {\
774 \"0000000\";\
775 \"0088880\";\
776 \"0888870\";\
777 \"07777f0\";\
778 \"0ffff00\";\
779 \"0000000\";\
780}\
781local layerForwardIcon = {\
782 \"0000000\";\
783 \"000fff0\";\
784 \"00777f0\";\
785 \"0888700\";\
786 \"0888000\";\
787 \"0000000\";\
788}\
789--The available menu options in the ctrl menu\
790local mChoices = {\"Save\",\"Exit\"}\
791--The available modes from the dropdown menu- tables indicate submenus (include a name!)\
792local ddModes = { { \"paint\", \"brush\", \"pippette\", \"flood\", \"move\", \"clear\", \"select\", name = \"painting\" }, { \"alpha to left\", \"alpha to right\", \"hide interface\", name = \"display\" }, \"help\", { \"print\", \"save\", \"exit\", name = \"file\" }, name = \"menu\" }\
793--The available modes from the selection right-click menu\
794local srModes = { \"cut\", \"copy\", \"paste\", \"clear\", \"hide\", name = \"selection\" }\
795--The list of available help topics for each mode 127\
796local helpTopics = {\
797 [1] = {\
798 name = \"Paint Mode\",\
799 key = nil,\
800 animonly = false,\
801 textonly = false,\
802 message = \"The default mode for NPaintPro, for painting pixels.\"\
803 ..\" Controls here that are not overridden will apply for all other modes. Leaving a mode by selecting that mode \"\
804 ..\" again will always send the user back to paint mode.\",\
805 controls = {\
806 { \"Arrow keys\", \"Scroll the canvas\" },\
807 { \"Left Click\", \"Paint/select left colour\" },\
808 { \"Right Click\", \"Paint/select right colour\" },\
809 { \"Z Key\", \"Clear image on screen\" },\
810 { \"Tab Key\", \"Hide selection rectangle if visible\" },\
811 { \"Q Key\", \"Set alpha mask to left colour\" },\
812 { \"W Key\", \"Set alpha mask to right colour\" },\
813 { \"Number Keys\", \"Swich between frames 1-9\" },\
814 { \"</> keys\", \"Move to the next/last frame\" },\
815 { \"R Key\", \"Removes every frame after the current frame\"}\
816 }\
817 },\
818 [2] = {\
819 name = \"Brush Mode\",\
820 key = \"b\",\
821 animonly = false,\
822 textonly = false,\
823 message = \"Brush mode allows painting a circular area of variable diameter rather than a single pixel, working in \"..\
824 \"the exact same way as paint mode in all other regards.\",\
825 controls = {\
826 { \"Left Click\", \"Paints a brush blob with the left colour\" },\
827 { \"Right Click\", \"Paints a brush blob with the right colour\" },\
828 { \"Number Keys\", \"Changes the radius of the brush blob from 2-9\" }\
829 }\
830 },\
831 [3] = {\
832 name = \"Pippette Mode\",\
833 key = \"p\",\
834 animonly = false,\
835 textonly = false,\
836 message = \"Pippette mode allows the user to click the canvas and set the colour clicked to the left or right \"..\
837 \"selected colour, for later painting.\",\
838 controls = {\
839 { \"Left Click\", \"Sets clicked colour to the left selected colour\" },\
840 { \"Right Click\", \"Sets clicked colour to the right selected colour\" }\
841 }\
842 },\
843 [4] = {\
844 name = \"Move Mode\",\
845 key = \"m\",\
846 animonly = false,\
847 textonly = false,\
848 message = \"Mode mode allows the moving of the entire image on the screen. This is especially useful for justifying\"..\
849 \" the image to the top-left for animations or game assets.\",\
850 controls = {\
851 { \"Left/Right Click\", \"Moves top-left corner of image to selected square\" },\
852 { \"Arrow keys\", \"Moves image one pixel in any direction\" }\
853 }\
854 },\
855 [5] = {\
856 name = \"Flood Mode\",\
857 key = \"f\",\
858 animonly = false,\
859 textonly = false,\
860 message = \"Flood mode allows the changing of an area of a given colour to that of the selected colour. \"..\
861 \"The tool uses a flood4 algorithm and will not fill diagonally. Transparency cannot be flood filled.\",\
862 controls = {\
863 { \"Left Click\", \"Flood fills selected area to left colour\" },\
864 { \"Right Click\", \"Flood fills selected area to right colour\" }\
865 }\
866 },\
867 [6] = {\
868 name = \"Select Mode\",\
869 key = \"s\",\
870 animonly = false,\
871 textonly = false,\
872 message = \"Select mode allows the creation and use of the selection rectangle, to highlight specific areas on \"..\
873 \"the screen and perform operations on the selected area of the image. The selection rectangle can contain an \"..\
874 \"image on the clipboard- if it does, the image will flash inside the rectangle, and the rectangle edges will \"..\
875 \"be light grey instead of dark grey.\",\
876 controls = {\
877 { \"C Key\", \"Copy: Moves selection into the clipboard\" },\
878 { \"X Key\", \"Cut: Clears canvas under the rectangle, and moves it into the clipboard\" },\
879 { \"V Key\", \"Paste: Copys clipboard to the canvas\" },\
880 { \"Z Key\", \"Clears clipboard\" },\
881 { \"Left Click\", \"Moves top-left corner of rectangle to selected pixel\" },\
882 { \"Right Click\", \"Opens selection menu\" },\
883 { \"Arrow Keys\", \"Moves rectangle one pixel in any direction\" }\
884 }\
885 },\
886 [7] = {\
887 name = \"Corner Select Mode\",\
888 key = nil,\
889 animonly = false,\
890 textonly = false,\
891 message = \"If a selection rectangle isn't visible, this mode is selected automatically. It allows the \"..\
892 \"defining of the corners of the rectangle- one the top-left and bottom-right corners have been defined, \"..\
893 \"NPaintPro switches to selection mode. Note rectangle must be at least 2 pixels wide and high.\",\
894 controls = {\
895 { \"Left/Right Click\", \"Defines a corner of the selection rectangle\" }\
896 }\
897 },\
898 [8] = {\
899 name = \"Play Mode\",\
900 key = \"space\",\
901 animonly = true,\
902 textonly = false,\
903 message = \"Play mode will loop through each frame in your animation at a constant rate. Editing tools are \"..\
904 \"locked in this mode, and the coordinate display will turn green to indicate it is on.\",\
905 controls = {\
906 { \"</> Keys\", \"Increases/Decreases speed of the animation\" },\
907 { \"Space Bar\", \"Returns to paint mode\" }\
908 }\
909 },\
910 [9] = {\
911 name = \"Record Mode\",\
912 key = \"\\\\\",\
913 animonly = true,\
914 textonly = false,\
915 message = \"Record mode is not a true mode, but influences how other modes work. Changes made that modify the \"..\
916 \"canvas in record mode will affect ALL frames in the animation. The coordinates will turn red to indicate that \"..\
917 \"record mode is on.\",\
918 controls = {\
919 { \"\", \"Affects:\" },\
920 { \"- Paint Mode\", \"\" },\
921 { \"- Brush Mode\", \"\" },\
922 { \"- Cut and Paste in Select Mode\", \"\"},\
923 { \"- Move Mode\", \"\"}\
924 }\
925 },\
926\009\009[10] = {\
927\009\009\009\009name = \"Hide Interface\",\
928\009\009\009\009key = \"~\",\
929\009\009\009\009animonly = false,\
930\009\009\009\009textonly = false,\
931\009\009\009\009message = \"Hides the sidebar and colour picker so only the image is visible.\"..\
932\009\009\009\009\" The program can be started with the interface hidden using the -d command line option.\"..\
933\009\009\009\009\" When hidden, if a file is animated it will automatically go to play mode.\\n\"..\
934\009\009\009\009\"Note that all other input is locked until the display is revealed again in this\"..\
935\009\009\009\009\" mode.\",\
936\009\009\009\009controls = {\
937 { \"</> Keys\", \"Increases/Decreases speed of the animation\" },\
938\009\009\009\009\009{ \"~ Key\", \"Shows interface\"}\
939\009\009\009\009}\
940\009\009},\
941 [11] = {\
942 name = \"Help Mode\",\
943 key = \"h\",\
944 animonly = false,\
945 textonly = false,\
946 message = \"Displays this help screen. Clicking on options will display help on that topic. Clicking out of the screen\"..\
947 \" will leave this mode.\",\
948 controls = {\
949 { \"Left/Right Click\", \"Displays a topic/Leaves the mode\" }\
950 }\
951 },\
952 [12] = {\
953 name = \"File Mode\",\
954 key = nil,\
955 animonly = false,\
956 textonly = false,\
957 message = \"Clicking on the mode display at the bottom of the screen will open the options menu. Here you can\"..\
958 \" activate all of the modes in the program with a simple mouse click. Pressing left control will open up the\"..\
959 \" file menu automatically.\",\
960 controls = {\
961 { \"leftCtrl\", \"Opens the file menu\" },\
962 { \"leftAlt\", \"Opens the paint menu\" }\
963 }\
964 },\
965 [13] = {\
966 name = \"Text Mode\",\
967 key = \"t\",\
968 animonly = false,\
969 textonly = true,\
970 message = \"In this mode, the user is able to type letters onto the document for display. The left colour \"..\
971 \"pallette value determines what colour the text will be, and the right determines what colour the background \"..\
972 \"will be (set either to nil to keep the same colours as already there).\",\
973 controls = {\
974 { \"Backspace\", \"Deletes the character on the previous line\" },\
975 { \"Arrow Keys\", \"Moves the cursor in any direction\" },\
976 { \"Left Click\", \"Moves the cursor to beneath the mouse cursor\" }\
977 }\
978 },\
979 [14] = {\
980 name = \"Textpaint Mode\",\
981 key = \"y\",\
982 animonly = false,\
983 textonly = true,\
984 message = \"Allows the user to paint any text on screen to the desired colour with the mouse. If affects the text colour\"..\
985 \" values rather than the background values, but operates identically to paint mode in all other regards.\",\
986 controls = {\
987 { \"Left Click\", \"Paints the text with the left colour\" },\
988 { \"Right Click\", \"Paints the text with the right colour\" }\
989 }\
990 },\
991 [15] = {\
992 name = \"About NPaintPro\",\
993 keys = nil,\
994 animonly = false,\
995 textonly = false,\
996 message = \"NPaintPro: The feature-bloated paint program for ComputerCraft by Nitrogen Fingers.\",\
997 controls = {\
998 { \"Testers:\", \" \"},\
999 { \" \", \"Faubiguy\"},\
1000 { \" \", \"TheOriginalBIT\"}\
1001 }\
1002 }\
1003}\
1004--The \"bounds\" of the image- the first/last point on both axes where a pixel appears\
1005local toplim,botlim,leflim,riglim = nil,nil,nil,nil\
1006--The selected path\
1007local sPath = nil\
1008\
1009\
1010--Screen Size Parameters- decided dynamically further down the program\
1011--Whether or not the help screen is available\
1012local helpAvailable = true\
1013--Whether or not the main menu is available\
1014local mainAvailable = true\
1015--Whether or not selection box dropdowns are available\
1016local boxdropAvailable = true\
1017--Whether or not a manual file descriptor option is available (part of the title)\
1018local filemakerAvailable = true\
1019 \
1020--[[ \
1021 Section: Helpers \
1022]]--\
1023 \
1024--[[Converts a colour parameter into a single-digit hex coordinate for the colour\
1025 Params: colour:int = The colour to be converted\
1026 Returns:string A string conversion of the colour\
1027]]--\
1028local function getHexOf(colour)\
1029 if not colour or not tonumber(colour) then\
1030 return \" \"\
1031 end\
1032 local value = math.log(colour)/math.log(2)\
1033 if value > 9 then\
1034 value = hexnums[value]\
1035 end\
1036 return value\
1037end\
1038 \
1039--[[Converts a hex digit into a colour value\
1040 Params: hex:?string = the hex digit to be converted\
1041 Returns:string A colour value corresponding to the hex, or nil if the character is invalid\
1042]]--\
1043local function getColourOf(hex)\
1044 local value = tonumber(hex, 16)\
1045 if not value then return nil end\
1046 value = math.pow(2,value)\
1047 return value\
1048end\
1049 \
1050--[[Finds the largest width and height of the text in a given menu. Should conform to the format\
1051\009of all standard menus (number indexed values and a 'name' field).\
1052\009This is done recursively. It's just easier that way.\
1053\009Params: menu:table = the table being tested for the max width and height\
1054\009Returns:number,number = the max width and height of the text or names of any menu or submenu.\
1055]]--\
1056local function findMaxWH(menu)\
1057\009local wmax,hmax = #menu.name, #menu\
1058\009for _,entry in pairs(menu) do\
1059\009\009if type(entry) == \"table\" then\
1060\009\009\009local nw,nh = findMaxWH(entry)\
1061\009\009\009wmax = math.max(wmax,nw)\
1062\009\009\009hmax = math.max(hmax,nh)\
1063\009\009else\
1064\009\009\009wmax = math.max(wmax,#entry)\
1065\009\009end\
1066\009end\
1067\009return wmax,hmax\
1068end\
1069 \
1070--[[Determines what services are available depending on the size of the screen. Certain features\
1071\009may be disabled with screen real estate does not allow for it.\
1072\009Params: none\
1073\009Returns:nil\
1074]]--\
1075local function determineAvailableServices()\
1076\009--Help files were designed to fit a 'standard' CC screen, of 51 x 19. The height of the screen\
1077\009--needs to match the number of available options plus white space, but for consistency with\
1078\009--the files themselves, a natural size of 51 is required for the screen width as well.\
1079\009if w < 51 or h < #helpTopics+3 then helpAvailable = false end\
1080\009if not helpAvailable then table.remove(ddModes,3) end\
1081\009--These hard-coded values mirror the drawLogo values, with extra consideration for the \
1082\009--additional menu options\
1083\009if h < 14 or w < 24 then filemakerAvailable = false end\
1084\009\
1085\009--Menus can't cover the picker and need 2 spaces for branches. 4 whitespace on X total.\
1086\009--Menus need a title and can't eclipse the footer. 2 whitespace on Y total.\
1087\009local wmin,hmin = findMaxWH(ddModes)\
1088\009if w < wmin+4 or h < hmin+2 then mainAvailable = false end\
1089\009wmin,hmin = findMaxWH(srModes)\
1090\009if w < wmin+4 or h < hmin+2 then boxdropAvailable = false end\
1091end\
1092 \
1093--[[Finds the biggest and smallest bounds of the image- the outside points beyond which pixels do not appear\
1094 These values are assigned to the \"lim\" parameters for access by other methods\
1095 Params: forAllFrames:bool = True if all frames should be used to find bounds, otherwise false or nil\
1096 Returns:nil\
1097]]--\
1098local function updateImageLims(forAllFrames)\
1099 local f,l = sFrame,sFrame\
1100 if forAllFrames == true then f,l = 1,framecount end\
1101 \
1102 toplim,botlim,leflim,riglim = nil,nil,nil,nil\
1103 for locf = f,l do\
1104 for y,_ in pairs(frames[locf]) do\
1105 if type(y) == \"number\" then\
1106 for x,_ in pairs(frames[locf][y]) do\
1107 if frames[locf][y][x] ~= nil then\
1108 if leflim == nil or x < leflim then leflim = x end\
1109 if toplim == nil or y < toplim then toplim = y end\
1110 if riglim == nil or x > riglim then riglim = x end\
1111 if botlim == nil or y > botlim then botlim = y end\
1112 end\
1113 end\
1114 end\
1115 end\
1116 end\
1117 \
1118 --There is just... no easier way to do this. It's horrible, but necessary\
1119 if textEnabled then\
1120 for locf = f,l do\
1121 for y,_ in pairs(frames[locf].text) do\
1122 for x,_ in pairs(frames[locf].text[y]) do\
1123 if frames[locf].text[y][x] ~= nil then\
1124 if leflim == nil or x < leflim then leflim = x end\
1125 if toplim == nil or y < toplim then toplim = y end\
1126 if riglim == nil or x > riglim then riglim = x end\
1127 if botlim == nil or y > botlim then botlim = y end\
1128 end\
1129 end\
1130 end\
1131 for y,_ in pairs(frames[locf].textcol) do\
1132 for x,_ in pairs(frames[locf].textcol[y]) do\
1133 if frames[locf].textcol[y][x] ~= nil then\
1134 if leflim == nil or x < leflim then leflim = x end\
1135 if toplim == nil or y < toplim then toplim = y end\
1136 if riglim == nil or x > riglim then riglim = x end\
1137 if botlim == nil or y > botlim then botlim = y end\
1138 end\
1139 end\
1140 end\
1141 end\
1142 end\
1143end\
1144 \
1145--[[Determines how much of each material is required for a print. Done each time printing is called.\
1146 Params: none\
1147 Returns:table A complete list of how much of each material is required.\
1148]]--\
1149function calculateMaterials()\
1150 updateImageLims(animated)\
1151 requiredMaterials = {}\
1152 for i=1,16 do\
1153 requiredMaterials[i] = 0\
1154 end\
1155 \
1156 if not toplim then return end\
1157 \
1158 for i=1,#frames do\
1159 for y = toplim, botlim do\
1160 for x = leflim, riglim do\
1161 if type(frames[i][y][x]) == \"number\" then\
1162 requiredMaterials[math.log10(frames[i][y][x])/math.log10(2) + 1] =\
1163 requiredMaterials[math.log10(frames[i][y][x])/math.log10(2) + 1] + 1\
1164 end \
1165 end\
1166 end\
1167 end\
1168end\
1169 \
1170 \
1171--[[Updates the rectangle blink timer. Should be called anywhere events are captured, along with a timer capture.\
1172 Params: nil\
1173 Returns:nil\
1174]]--\
1175local function updateTimer(id)\
1176 if id == recttimer then\
1177 recttimer = os.startTimer(0.5)\
1178 rectblink = (rectblink % 2) + 1\
1179 end\
1180end\
1181 \
1182--[[Constructs a message based on the state currently selected\
1183 Params: nil\
1184 Returns:string A message regarding the state of the application\
1185]]--\
1186local function getStateMessage()\
1187 local msg = \" \"..string.upper(string.sub(state, 1, 1))..string.sub(state, 2, #state)..\" mode\"\
1188 if state == \"brush\" then msg = msg..\", size=\"..brushsize end\
1189 return msg\
1190end\
1191 \
1192--[[Calls the rednet_message event, but also looks for timer events to keep then\
1193 system timer ticking.\
1194 Params: timeout:number how long before the event times out\
1195 Returns:number the id of the sender\
1196 :number the message send\
1197]]--\
1198local function rsTimeReceive(timeout)\
1199 local timerID\
1200 if timeout then timerID = os.startTimer(timeout) end\
1201 \
1202 local id,key,msg = nil,nil\
1203 while true do\
1204 id,key,msg = os.pullEvent()\
1205 \
1206 if id == \"timer\" then\
1207 if key == timerID then return\
1208 else updateTimer(key) end\
1209 end\
1210 if id == \"rednet_message\" then\
1211 return key,msg\
1212 end\
1213 end\
1214end\
1215 \
1216--[[Draws a picture, in paint table format on the screen\
1217 Params: image:table = the image to display\
1218 xinit:number = the x position of the top-left corner of the image\
1219 yinit:number = the y position of the top-left corner of the image\
1220 alpha:number = the color to display for the alpha channel. Default is white.\
1221 Returns:nil\
1222]]--\
1223local function drawPictureTable(image, xinit, yinit, alpha)\
1224 if not alpha then alpha = 1 end\
1225 for y=1,#image do\
1226 for x=1,#image[y] do\
1227 term.setCursorPos(xinit + x-1, yinit + y-1)\
1228 local col = getColourOf(string.sub(image[y], x, x))\
1229 if not col then col = alpha end\
1230 term.setBackgroundColour(col)\
1231 term.write(\" \")\
1232 end\
1233 end\
1234end\
1235 \
1236--[[ \
1237 Section: Loading \
1238]]--\
1239 \
1240--[[Loads a non-animted paint file into the program\
1241 Params: path:string = The path in which the file is located\
1242 Returns:nil\
1243]]--\
1244local function loadNFP(path)\
1245 sFrame = 1\
1246 frames[sFrame] = { }\
1247 if fs.exists(path) then\
1248 local file = io.open(path, \"r\" )\
1249 local sLine = file:read()\
1250 local num = 1\
1251 while sLine do\
1252 table.insert(frames[sFrame], num, {})\
1253 for i=1,#sLine do\
1254 frames[sFrame][num][i] = getColourOf(string.sub(sLine,i,i))\
1255 end\
1256 num = num+1\
1257 sLine = file:read()\
1258 end\
1259 file:close()\
1260 end\
1261end\
1262 \
1263--[[Loads a text-paint file into the program\
1264 Params: path:string = The path in which the file is located\
1265 Returns:nil\
1266]]--\
1267local function loadNFT(path)\
1268 sFrame = 1\
1269 frames[sFrame] = { }\
1270 frames[sFrame].text = { }\
1271 frames[sFrame].textcol = { }\
1272 \
1273 if fs.exists(path) then\
1274 local file = io.open(path, \"r\")\
1275 local sLine = file:read()\
1276 local num = 1\
1277 while sLine do\
1278 table.insert(frames[sFrame], num, {})\
1279 table.insert(frames[sFrame].text, num, {})\
1280 table.insert(frames[sFrame].textcol, num, {})\
1281 \
1282 --As we're no longer 1-1, we keep track of what index to write to\
1283 local writeIndex = 1\
1284 --Tells us if we've hit a 30 or 31 (BG and FG respectively)- next char specifies the curr colour\
1285 local bgNext, fgNext = false, false\
1286 --The current background and foreground colours\
1287 local currBG, currFG = nil,nil\
1288 term.setCursorPos(1,1)\
1289 for i=1,#sLine do\
1290 local nextChar = string.sub(sLine, i, i)\
1291 if nextChar:byte() == 30 then\
1292 bgNext = true\
1293 elseif nextChar:byte() == 31 then\
1294 fgNext = true\
1295 elseif bgNext then\
1296 currBG = getColourOf(nextChar)\
1297 bgNext = false\
1298 elseif fgNext then\
1299 currFG = getColourOf(nextChar)\
1300 fgNext = false\
1301 else\
1302 if nextChar ~= \" \" and currFG == nil then\
1303 currFG = colours.white\
1304 end\
1305 frames[sFrame][num][writeIndex] = currBG\
1306 frames[sFrame].textcol[num][writeIndex] = currFG\
1307 frames[sFrame].text[num][writeIndex] = nextChar\
1308 writeIndex = writeIndex + 1\
1309 end\
1310 end\
1311 num = num+1\
1312 sLine = file:read()\
1313 end\
1314 file:close()\
1315 end\
1316end\
1317 \
1318--[[Loads an animated paint file into the program\
1319 Params: path:string = The path in which the file is located\
1320 Returns:nil\
1321]]--\
1322local function loadNFA(path)\
1323 frames[sFrame] = { }\
1324 if fs.exists(path) then\
1325 local file = io.open(path, \"r\" )\
1326 local sLine = file:read()\
1327 local num = 1\
1328 while sLine do\
1329 table.insert(frames[sFrame], num, {})\
1330 if sLine == \"~\" then\
1331 sFrame = sFrame + 1\
1332 frames[sFrame] = { }\
1333 num = 1\
1334 else\
1335 for i=1,#sLine do\
1336 frames[sFrame][num][i] = getColourOf(string.sub(sLine,i,i))\
1337 end\
1338 num = num+1\
1339 end\
1340 sLine = file:read()\
1341 end\
1342 file:close()\
1343 end\
1344 framecount = sFrame\
1345 sFrame = 1\
1346end\
1347 \
1348--[[Saves a non-animated paint file to the specified path\
1349 Params: path:string = The path to save the file to\
1350 Returns:nil\
1351]]--\
1352local function saveNFP(path)\
1353 local sDir = string.sub(sPath, 1, #sPath - #fs.getName(sPath))\
1354 if not fs.exists(sDir) then\
1355 fs.makeDir(sDir)\
1356 end\
1357 \
1358 local file = io.open(path, \"w\")\
1359 updateImageLims(false)\
1360 if not toplim then\
1361 file:close()\
1362 return\
1363 end\
1364 for y=1,botlim do\
1365 local line = \"\"\
1366 if frames[sFrame][y] then\
1367 for x=1,riglim do\
1368 line = line..getHexOf(frames[sFrame][y][x])\
1369 end\
1370 end\
1371 file:write(line..\"\\n\")\
1372 end\
1373 file:close()\
1374end\
1375 \
1376--[[Saves a text-paint file to the specified path\
1377 Params: path:string = The path to save the file to\
1378 Returns:nil\
1379]]--\
1380local function saveNFT(path)\
1381 local sDir = string.sub(sPath, 1, #sPath - #fs.getName(sPath))\
1382 if not fs.exists(sDir) then\
1383 fs.makeDir(sDir)\
1384 end\
1385 \
1386 local file = io.open(path, \"w\")\
1387 updateImageLims(false)\
1388 if not toplim then\
1389 file:close()\
1390 return\
1391 end\
1392 for y=1,botlim do\
1393 local line = \"\"\
1394 local currBG, currFG = nil,nil\
1395 for x=1,riglim do\
1396 if frames[sFrame][y] and frames[sFrame][y][x] ~= currBG then\
1397 line = line..string.char(30)..getHexOf(frames[sFrame][y][x])\
1398 currBG = frames[sFrame][y][x]\
1399 end\
1400 if frames[sFrame].textcol[y] and frames[sFrame].textcol[y][x] ~= currFG then\
1401 line = line..string.char(31)..getHexOf(frames[sFrame].textcol[y][x])\
1402 currFG = frames[sFrame].textcol[y][x]\
1403 end\
1404 if frames[sFrame].text[y] then\
1405 local char = frames[sFrame].text[y][x]\
1406 if not char then char = \" \" end\
1407 line = line..char\
1408 end\
1409 end\
1410 file:write(line..\"\\n\")\
1411 end\
1412 file:close()\
1413end\
1414 \
1415--[[Saves a animated paint file to the specified path\
1416 Params: path:string = The path to save the file to\
1417 Returns:nil\
1418]]--\
1419local function saveNFA(path)\
1420 local sDir = string.sub(sPath, 1, #sPath - #fs.getName(sPath))\
1421 if not fs.exists(sDir) then\
1422 fs.makeDir(sDir)\
1423 end\
1424 \
1425 local file = io.open(path, \"w\")\
1426 updateImageLims(true)\
1427 if not toplim then\
1428 file:close()\
1429 return\
1430 end\
1431 for i=1,#frames do\
1432 for y=1,botlim do\
1433 local line = \"\"\
1434 if frames[i][y] then\
1435 for x=1,riglim do\
1436 line = line..getHexOf(frames[i][y][x])\
1437 end\
1438 end\
1439 file:write(line..\"\\n\")\
1440 end\
1441 if i < #frames then file:write(\"~\\n\") end\
1442 end\
1443 file:close()\
1444end\
1445\
1446--[[Runs a special pre-program dialogue to determine the filename and filepath. Done if\
1447\009there's room, and a file name hasn't been specified\
1448\009Params: none\
1449\009Returns:bool= true if file is created; false otherwise\
1450]]--\
1451local function runFileMaker()\
1452\009local newFName = \"\"\
1453\009local fileType = 1\
1454\009if animated then fileType = 2\
1455\009elseif textEnabled then fileType = 3 end\
1456\009\
1457\009local tlx,tly = math.floor(w/2 - #logo[1]/2), math.floor(h/2 + #logo/2 + 1)\
1458\
1459\009--This is done on top of the logo, so it backpedals a bit.\
1460\009term.setCursorPos(tlx, tly)\
1461\009term.clearLine()\
1462\009term.write(\"Name: \")\
1463\009term.setCursorPos(tlx, tly + 1)\
1464\009term.clearLine()\
1465\009term.write(\"Filetype: Sprite\")\
1466\009term.setCursorPos(tlx + 12, tly + 2)\
1467\009term.write(\"Animation\")\
1468\009term.setCursorPos(tlx + 12, tly + 3)\
1469\009term.write(\"Text\")\
1470\009\
1471\009while true do\
1472\009\009term.setCursorPos(tlx + 6, tly)\
1473\009\009term.setBackgroundColour(colours.lightGrey)\
1474\009\009term.setTextColour(colours.grey)\
1475\009\009term.write(newFName..string.rep(\" \", 15-#newFName))\
1476\009\009term.setBackgroundColour(colours.white)\
1477\009\009term.setTextColour(colours.black)\
1478\009\009local extension = \".nfp\"\
1479\009\009if fileType == 2 then extension = \".nfa\"\
1480\009\009elseif fileType == 3 then extension = \".nft\" end\
1481\009\009term.write(extension)\
1482\009\009\
1483\009\009term.setBackgroundColour(colours.lightGrey)\
1484\009\009term.setTextColour(colours.grey)\
1485\009\009for i=1,3 do\
1486\009\009\009term.setCursorPos(tlx + 24, tly + i)\
1487\009\009\009if i==fileType then term.write(\"X\")\
1488\009\009\009else term.write(\" \") end\
1489\009\009end\
1490\009\009\
1491\009\009local fPath = shell.resolve(newFName..extension)\
1492\009\009term.setCursorPos(tlx, tly + 3)\
1493\009\009local fileValid = true\
1494\009\009if (fs.exists(fPath) and fs.isDir(fPath)) or newFName == \"\" then\
1495\009\009\009term.setBackgroundColour(colours.white)\
1496\009\009\009term.setTextColour(colours.red)\
1497\009\009\009term.write(\"Invalid \")\
1498\009\009\009fileValid = false\
1499\009\009elseif fs.exists(fPath) then\
1500\009\009\009term.setBackgroundColour(colours.grey)\
1501\009\009\009term.setTextColour(colours.lightGrey)\
1502\009\009\009term.write(\" Edit \")\
1503\009\009else\
1504\009\009\009term.setBackgroundColour(colours.grey)\
1505\009\009\009term.setTextColour(colours.lightGrey)\
1506\009\009\009term.write(\" Create \")\
1507\009\009end\
1508\009\009\
1509\009\009term.setTextColour(colours.grey)\
1510\009\009term.setCursorPos(tlx + 6 + #newFName, tly)\
1511\009\009term.setCursorBlink(true)\
1512\009\009\
1513\009\009local id,p1,p2,p3 = os.pullEvent()\
1514\009\009if id == \"key\" then\
1515\009\009\009if p1 == keys.backspace and #newFName > 0 then \
1516\009\009\009\009newFName = string.sub(newFName, 1, #newFName-1)\
1517\009\009\009elseif p1 == keys.enter and fileValid then\
1518\009\009\009\009sPath = fPath\
1519\009\009\009\009return true\
1520\009\009\009end\
1521\009\009elseif id == \"char\" and p1 ~= \".\" and p1 ~= \" \" and #newFName < 15 then\
1522\009\009\009newFName = newFName..p1\
1523\009\009elseif id == \"mouse_click\" then\
1524\009\009\009--The option boxes. Man, hardcoding is ugly...\
1525\009\009\009if p2 == tlx + 24 then\
1526\009\009\009\009for i=1,3 do \
1527\009\009\009\009\009if p3 == tly+i then fileType = i end \
1528\009\009\009\009end\
1529\009\009\009end\
1530\009\009\009if p3 == tly + 3 and p2 >= tlx and p2 <= tlx + 8 and fileValid then\
1531\009\009\009\009sPath = fPath\
1532\009\009\009\009return true\
1533\009\009\009end\
1534\009\009end\
1535\009end\
1536end\
1537 \
1538--[[Initializes the program, by loading in the paint file. Called at the start of each program.\
1539 Params: none\
1540 Returns:nil\
1541]]--\
1542local function init()\
1543 if textEnabled then\
1544 loadNFT(sPath)\
1545 table.insert(ddModes, 2, { \"text\", \"textpaint\", name = \"text\"})\
1546 elseif animated then\
1547 loadNFA(sPath)\
1548 table.insert(ddModes, #ddModes, { \"record\", \"play\", name = \"anim\" })\
1549 table.insert(ddModes, #ddModes, { \"go to\", \"remove\", name = \"frames\"})\
1550 table.insert(ddModes[2], #ddModes[2], \"blueprint on\")\
1551 table.insert(ddModes[2], #ddModes[2], \"layers on\")\
1552 else\
1553 loadNFP(sPath)\
1554 table.insert(ddModes[2], #ddModes[2], \"blueprint on\")\
1555 end\
1556\009\009\
1557 for i=0,15 do\
1558 table.insert(column, math.pow(2,i))\
1559 end\
1560end\
1561 \
1562--[[ \
1563 Section: Drawing \
1564]]--\
1565 \
1566 \
1567--[[Draws the rather superflous logo. Takes about 1 second, before user is able to move to the\
1568 actual program.\
1569 Params: none\
1570 Returns:bool= true if the file select ran successfully; false otherwise.\
1571]]--\
1572local function drawLogo()\
1573 term.setBackgroundColour(colours.white)\
1574 term.clear()\
1575\009\009if h >= 12 and w >= 24 then\
1576\009\009\009drawPictureTable(logo, w/2 - #logo[1]/2, h/2 - #logo/2, colours.white)\
1577\009\009\009term.setBackgroundColour(colours.white)\
1578\009\009\009term.setTextColour(colours.black)\
1579\009\009\009local msg = \"NPaintPro\"\
1580\009\009\009term.setCursorPos(w/2 - #msg/2, h/2 + #logo/2 + 1)\
1581\009\009\009term.write(msg)\
1582\009\009\009msg = \"By NitrogenFingers\"\
1583\009\009\009term.setCursorPos(w/2 - #msg/2, h/2 + #logo/2 + 2)\
1584\009\009\009term.write(msg)\
1585 elseif w >= 15 then\
1586\009\009\009local msg = \"NPaintPro\"\
1587\009\009\009term.setCursorPos(math.ceil(w/2 - #msg/2), h/2)\
1588\009\009\009term.setTextColour(colours.cyan)\
1589\009\009\009term.write(msg)\
1590\009\009\009msg = \"NitrogenFingers\"\
1591\009\009\009term.setCursorPos(math.ceil(w/2 - #msg/2), h/2 + 1)\
1592\009\009\009term.setTextColour(colours.black)\
1593\009\009\009term.write(msg)\
1594\009\009else\
1595\009\009\009local msg = \"NPP\"\
1596\009\009\009term.setCursorPos(math.ceil(w/2 - #msg/2), math.floor(h/2))\
1597\009\009\009term.setTextColour(colours.cyan)\
1598\009\009\009term.write(msg)\
1599\009\009\009msg = \"By NF\"\
1600\009\009\009term.setCursorPos(math.ceil(w/2 - #msg/2), math.ceil(h/2))\
1601\009\009\009term.setTextColour(colours.black)\
1602\009\009\009term.write(msg)\
1603\009\009end\
1604 os.pullEvent()\
1605end\
1606 \
1607--[[Clears the display to the alpha channel colour, draws the canvas, the image buffer and the selection\
1608 rectanlge if any of these things are present.\
1609 Params: none\
1610 Returns:nil\
1611]]--\
1612local function drawCanvas()\
1613 --We have to readjust the position of the canvas if we're printing\
1614 turtlechar = \"@\"\
1615 if state == \"active print\" then\
1616 if layering == \"up\" then\
1617 if py >= 1 and py <= #frames then\
1618 sFrame = py\
1619 end\
1620 if pz < sy then sy = pz\
1621 elseif pz > sy + h - 1 then sy = pz + h - 1 end\
1622 if px < sx then sx = px\
1623 elseif px > sx + w - 2 then sx = px + w - 2 end\
1624 else\
1625 if pz >= 1 and pz <= #frames then\
1626 sFrame = pz\
1627 end\
1628 \
1629 if py < sy then sy = py\
1630 elseif py > sy + h - 1 then sy = py + h - 1 end\
1631 if px < sx then sx = px\
1632 elseif px > sx + w - 2 then sx = px + w - 2 end\
1633 end\
1634 \
1635 if pfx == 1 then turtlechar = \">\"\
1636 elseif pfx == -1 then turtlechar = \"<\"\
1637 elseif pfz == 1 then turtlechar = \"V\"\
1638 elseif pfz == -1 then turtlechar = \"^\"\
1639 end\
1640 end\
1641 \
1642 --Picture next\
1643 local topLayer, botLayer\
1644 if layerDisplay then\
1645 topLayer = sFrame\
1646 botLayer = 1\
1647 else\
1648 topLayer,botLayer = sFrame,sFrame\
1649 end\
1650 \
1651\009 --How far the canvas draws. If the interface is visible, it stops short of that.\
1652\009 local wlim,hlim = 0,0\
1653\009\009if not interfaceHidden then\
1654\009\009\009wlim = 2\
1655\009\009\009hlim = 1\
1656\009\009end\
1657\009 \
1658 for currframe = botLayer,topLayer,1 do\
1659 for y=sy+1,sy+h-hlim do\
1660 if frames[currframe][y] then\
1661 for x=sx+1,sx+w-wlim do\
1662 term.setCursorPos(x-sx,y-sy)\
1663 if frames[currframe][y][x] then\
1664 term.setBackgroundColour(frames[currframe][y][x])\
1665 if textEnabled and frames[currframe].textcol[y][x] and frames[currframe].text[y][x] then\
1666 term.setTextColour(frames[currframe].textcol[y][x])\
1667 term.write(frames[currframe].text[y][x])\
1668 else\
1669 term.write(\" \")\
1670 end\
1671 else\
1672 tileExists = false\
1673 for i=currframe-1,botLayer,-1 do\
1674 if frames[i][y][x] then\
1675 tileExists = true\
1676 break\
1677 end\
1678 end\
1679 \
1680 if not tileExists then\
1681 if blueprint then\
1682 term.setBackgroundColour(colours.blue)\
1683 term.setTextColour(colours.white)\
1684 if x == sx+1 and y % 4 == 1 then\
1685 term.write(\"\"..((y/4) % 10))\
1686 elseif y == sy + 1 and x % 4 == 1 then\
1687 term.write(\"\"..((x/4) % 10))\
1688 elseif x % 2 == 1 and y % 2 == 1 then\
1689 term.write(\"+\")\
1690 elseif x % 2 == 1 then\
1691 term.write(\"|\")\
1692 elseif y % 2 == 1 then\
1693 term.write(\"-\")\
1694 else\
1695 term.write(\" \")\
1696 end\
1697 else\
1698 term.setBackgroundColour(alphaC)\
1699 if textEnabled and frames[currframe].textcol[y][x] and frames[currframe].text[y][x] then\
1700 term.setTextColour(frames[currframe].textcol[y][x])\
1701 term.write(frames[currframe].text[y][x])\
1702 else\
1703 term.write(\" \")\
1704 end\
1705 end\
1706 end\
1707 end\
1708 end\
1709 else\
1710 for x=sx+1,sx+w-wlim do\
1711 term.setCursorPos(x-sx,y-sy)\
1712 \
1713 tileExists = false\
1714 for i=currframe-1,botLayer,-1 do\
1715 if frames[i][y] and frames[i][y][x] then\
1716 tileExists = true\
1717 break\
1718 end\
1719 end\
1720 \
1721 if not tileExists then\
1722 if blueprint then\
1723 term.setBackgroundColour(colours.blue)\
1724 term.setTextColour(colours.white)\
1725 if x == sx+1 and y % 4 == 1 then\
1726 term.write(\"\"..((y/4) % 10))\
1727 elseif y == sy + 1 and x % 4 == 1 then\
1728 term.write(\"\"..((x/4) % 10))\
1729 elseif x % 2 == 1 and y % 2 == 1 then\
1730 term.write(\"+\")\
1731 elseif x % 2 == 1 then\
1732 term.write(\"|\")\
1733 elseif y % 2 == 1 then\
1734 term.write(\"-\")\
1735 else\
1736 term.write(\" \")\
1737 end\
1738 else\
1739 term.setBackgroundColour(alphaC)\
1740 term.write(\" \")\
1741 end\
1742 end\
1743 end\
1744 end\
1745 end\
1746 end\
1747 \
1748 --Then the printer, if he's on\
1749 if state == \"active print\" then\
1750 local bgColour = alphaC\
1751 if layering == \"up\" then\
1752 term.setCursorPos(px-sx,pz-sy)\
1753 if frames[sFrame] and frames[sFrame][pz-sy] and frames[sFrame][pz-sy][px-sx] then\
1754 bgColour = frames[sFrame][pz-sy][px-sx]\
1755 elseif blueprint then bgColour = colours.blue end\
1756 else\
1757 term.setCursorPos(px-sx,py-sy)\
1758 if frames[sFrame] and frames[sFrame][py-sy] and frames[sFrame][py-sy][px-sx] then\
1759 bgColour = frames[sFrame][py-sy][px-sx]\
1760 elseif blueprint then bgColour = colours.blue end\
1761 end\
1762 \
1763 term.setBackgroundColour(bgColour)\
1764 if bgColour == colours.black then term.setTextColour(colours.white)\
1765 else term.setTextColour(colours.black) end\
1766 \
1767 term.write(turtlechar)\
1768 end\
1769 \
1770 --Then the buffer\
1771 if selectrect then\
1772 if buffer and rectblink == 1 then\
1773 for y=selectrect.y1, math.min(selectrect.y2, selectrect.y1 + buffer.height-1) do\
1774 for x=selectrect.x1, math.min(selectrect.x2, selectrect.x1 + buffer.width-1) do\
1775 if buffer.contents[y-selectrect.y1+1][x-selectrect.x1+1] then\
1776 term.setCursorPos(x+sx,y+sy)\
1777 term.setBackgroundColour(buffer.contents[y-selectrect.y1+1][x-selectrect.x1+1])\
1778 term.write(\" \")\
1779 end\
1780 end\
1781 end\
1782 end\
1783 \
1784 --This draws the \"selection\" box\
1785 local add = nil\
1786 if buffer then\
1787 term.setBackgroundColour(colours.lightGrey)\
1788 else\
1789 term.setBackgroundColour(colours.grey)\
1790 end\
1791 for i=selectrect.x1, selectrect.x2 do\
1792 add = (i + selectrect.y1 + rectblink) % 2 == 0\
1793 term.setCursorPos(i-sx,selectrect.y1-sy)\
1794 if add then term.write(\" \") end\
1795 add = (i + selectrect.y2 + rectblink) % 2 == 0\
1796 term.setCursorPos(i-sx,selectrect.y2-sy)\
1797 if add then term.write(\" \") end\
1798 end\
1799 for i=selectrect.y1 + 1, selectrect.y2 - 1 do\
1800 add = (i + selectrect.x1 + rectblink) % 2 == 0\
1801 term.setCursorPos(selectrect.x1-sx,i-sy)\
1802 if add then term.write(\" \") end\
1803 add = (i + selectrect.x2 + rectblink) % 2 == 0\
1804 term.setCursorPos(selectrect.x2-sx,i-sy)\
1805 if add then term.write(\" \") end\
1806 end\
1807 end\
1808end\
1809 \
1810--[[Draws the colour picker on the right side of the screen, the colour pallette and the footer with any\
1811 messages currently being displayed\
1812 Params: none\
1813 Returns:nil\
1814]]--\
1815local function drawInterface()\
1816 --Picker\
1817\009\009local coffset,ioffset = 0,0\
1818\009\009local maxcsize = h-2\
1819\009\009if h < #column + 2 then\
1820\009\009\009maxcsize = h-4\
1821\009\009\009coffset = columnoffset\
1822\009\009\009ioffset = 1\
1823\009\009\009term.setBackgroundColour(colours.lightGrey)\
1824\009\009\009term.setTextColour(colours.grey)\
1825\009\009\009term.setCursorPos(w-1,1)\
1826\009\009\009term.write(\"^^\")\
1827\009\009\009term.setCursorPos(w-1,h-2)\
1828\009\009\009term.write(\"VV\")\
1829\009\009end\
1830 for i=1,math.min(#column+1,maxcsize) do\
1831\009\009\009term.setCursorPos(w-1, i + ioffset)\
1832\009\009\009local ci = i+coffset\
1833\009\009\009if ci == #column+1 then\
1834\009\009\009\009term.setBackgroundColour(colours.black)\
1835\009\009\009\009term.setTextColour(colours.red)\
1836\009\009\009\009term.write(\"XX\")\
1837\009\009\009elseif state == \"print\" then\
1838\009\009\009\009term.setBackgroundColour(column[ci])\
1839\009\009\009\009if column[ci] == colours.black then\
1840\009\009\009\009\009term.setTextColour(colours.white)\
1841\009\009\009\009else term.setTextColour(colours.black) end\
1842\009\009\009\009\
1843\009\009\009\009if requirementsDisplayed then\
1844\009\009\009\009\009\009if requiredMaterials[i] < 10 then term.write(\" \") end\
1845\009\009\009\009\009\009term.setCursorPos(w-#tostring(requiredMaterials[i])+1, i)\
1846\009\009\009\009\009\009term.write(requiredMaterials[i])\
1847\009\009\009\009else\
1848\009\009\009\009\009\009if i+coffset < 10 then term.write(\" \") end\
1849\009\009\009\009\009\009term.write(i+coffset)\
1850\009\009\009\009end\
1851\009\009\009else\
1852\009\009\009\009term.setBackgroundColour(column[ci])\
1853\009\009\009\009term.write(\" \")\
1854\009\009\009end\
1855 end\
1856\009\009--Filling the whitespace with... 'greyspace' *shudder*\
1857\009\009if h > #column+3 then\
1858\009\009\009term.setTextColour(colours.grey)\
1859\009\009\009term.setBackgroundColour(colours.lightGrey)\
1860\009\009\009for y=#column+2,h-2 do\
1861\009\009\009\009term.setCursorPos(w-1,y)\
1862\009\009\009\009term.write(\"| \")\
1863\009\009\009end\
1864\009\009end\
1865 --Pallette\
1866 term.setCursorPos(w-1,h-1)\
1867 if not lSel then\
1868 term.setBackgroundColour(colours.black)\
1869 term.setTextColour(colours.red)\
1870 term.write(\"X\")\
1871 else\
1872 term.setBackgroundColour(lSel)\
1873 term.setTextColour(lSel)\
1874 term.write(\" \")\
1875 end\
1876 if not rSel then\
1877 term.setBackgroundColour(colours.black)\
1878 term.setTextColour(colours.red)\
1879 term.write(\"X\")\
1880 else\
1881 term.setBackgroundColour(rSel)\
1882 term.setTextColour(rSel)\
1883 term.write(\" \")\
1884 end\
1885 --Footer\
1886 if inMenu then return end\
1887 \
1888 term.setCursorPos(1, h)\
1889 term.setBackgroundColour(colours.lightGrey)\
1890 term.setTextColour(colours.grey)\
1891 term.clearLine()\
1892\009\009if mainAvailable then\
1893\009\009\009if inDropDown then\
1894\009\009\009\009\009term.write(string.rep(\" \", #ddModes.name + 2))\
1895\009\009\009else\
1896\009\009\009\009\009term.setBackgroundColour(colours.grey)\
1897\009\009\009\009\009term.setTextColour(colours.lightGrey)\
1898\009\009\009\009\009term.write(ddModes.name..\" \")\
1899\009\009\009end\
1900\009\009end\
1901 term.setBackgroundColour(colours.lightGrey)\
1902 term.setTextColour(colours.grey)\
1903 term.write(getStateMessage())\
1904 \
1905 local coords=\"X:\"..sx..\" Y:\"..sy\
1906 if animated then coords = coords..\" Frame:\"..sFrame..\"/\"..framecount..\" \" end\
1907 term.setCursorPos(w-#coords+1,h)\
1908 if state == \"play\" then term.setBackgroundColour(colours.lime)\
1909 elseif record then term.setBackgroundColour(colours.red) end\
1910 term.write(coords)\
1911 \
1912 if animated then\
1913 term.setCursorPos(w-1,h)\
1914 term.setBackgroundColour(colours.grey)\
1915 term.setTextColour(colours.lightGrey)\
1916 term.write(\"<>\")\
1917 end\
1918end\
1919 \
1920--[[Runs an interface where users can select topics of help. Will return once the user quits the help screen.\
1921 Params: none\
1922 Returns:nil\
1923]]--\
1924local function drawHelpScreen()\
1925 local selectedHelp = nil\
1926 while true do\
1927 term.setBackgroundColour(colours.lightGrey)\
1928 term.clear()\
1929 if not selectedHelp then\
1930 term.setCursorPos(4, 1)\
1931 term.setTextColour(colours.brown)\
1932 term.write(\"Available modes (click for info):\")\
1933 for i=1,#helpTopics do\
1934 term.setCursorPos(2, 2 + i)\
1935 term.setTextColour(colours.black)\
1936 term.write(helpTopics[i].name)\
1937 if helpTopics[i].key then\
1938 term.setTextColour(colours.red)\
1939 term.write(\" (\"..helpTopics[i].key..\")\")\
1940 end\
1941 end\
1942 term.setCursorPos(4,h)\
1943 term.setTextColour(colours.black)\
1944 term.write(\"Press any key to exit\")\
1945 else\
1946 term.setCursorPos(4,1)\
1947 term.setTextColour(colours.brown)\
1948 term.write(helpTopics[selectedHelp].name)\
1949 if helpTopics[selectedHelp].key then\
1950 term.setTextColour(colours.red)\
1951 term.write(\" (\"..helpTopics[selectedHelp].key..\")\")\
1952 end\
1953 term.setCursorPos(1,3)\
1954 term.setTextColour(colours.black)\
1955 print(helpTopics[selectedHelp].message..\"\\n\")\
1956 for i=1,#helpTopics[selectedHelp].controls do\
1957 term.setTextColour(colours.brown)\
1958 term.write(helpTopics[selectedHelp].controls[i][1]..\" \")\
1959 term.setTextColour(colours.black)\
1960 print(helpTopics[selectedHelp].controls[i][2])\
1961 end\
1962 end\
1963 \
1964 local id,p1,p2,p3 = os.pullEvent()\
1965 \
1966 if id == \"timer\" then updateTimer(p1)\
1967 elseif id == \"key\" then\
1968 if selectedHelp then selectedHelp = nil\
1969 else break end\
1970 elseif id == \"mouse_click\" then\
1971 if not selectedHelp then\
1972 if p3 >=3 and p3 <= 2+#helpTopics then\
1973 selectedHelp = p3-2\
1974 else break end\
1975 else\
1976 selectedHelp = nil\
1977 end\
1978 end\
1979 end\
1980end\
1981 \
1982--[[Draws a message in the footer bar. A helper for DrawInterface, but can be called for custom messages, if the\
1983 inMenu paramter is set to true while this is being done (remember to set it back when done!)\
1984 Params: message:string = The message to be drawn\
1985 Returns:nil\
1986]]--\
1987local function drawMessage(message)\
1988 term.setCursorPos(1,h)\
1989 term.setBackgroundColour(colours.lightGrey)\
1990 term.setTextColour(colours.grey)\
1991 term.clearLine()\
1992 term.write(message)\
1993end\
1994 \
1995--[[\
1996 Section: Generic Interfaces\
1997]]--\
1998 \
1999 \
2000--[[One of my generic text printing methods, printing a message at a specified position with width and offset.\
2001 No colour materials included.\
2002 Params: msg:string = The message to print off-center\
2003 height:number = The starting height of the message\
2004 width:number = The limit as to how many characters long each line may be\
2005 offset:number = The starting width offset of the message\
2006 Returns:number the number of lines used in printing the message\
2007]]--\
2008local function wprintOffCenter(msg, height, width, offset)\
2009 local inc = 0\
2010 local ops = 1\
2011 while #msg - ops > width do\
2012 local nextspace = 0\
2013 while string.find(msg, \" \", ops + nextspace) and\
2014 string.find(msg, \" \", ops + nextspace) - ops < width do\
2015 nextspace = string.find(msg, \" \", nextspace + ops) + 1 - ops\
2016 end\
2017 local ox,oy = term.getCursorPos()\
2018 term.setCursorPos(width/2 - (nextspace)/2 + offset, height + inc)\
2019 inc = inc + 1\
2020 term.write(string.sub(msg, ops, nextspace + ops - 1))\
2021 ops = ops + nextspace\
2022 end\
2023 term.setCursorPos(width/2 - #string.sub(msg, ops)/2 + offset, height + inc)\
2024 term.write(string.sub(msg, ops))\
2025 \
2026 return inc + 1\
2027end\
2028 \
2029--[[Draws a message that must be clicked on or a key struck to be cleared. No options, so used for displaying\
2030 generic information.\
2031 Params: ctitle:string = The title of the confirm dialogue\
2032 msg:string = The message displayed in the dialogue\
2033 Returns:nil\
2034]]--\
2035local function displayConfirmDialogue(ctitle, msg)\
2036 local dialogoffset = 8\
2037 --We actually print twice- once to get the lines, second time to print proper. Easier this way.\
2038 local lines = wprintOffCenter(msg, 5, w - (dialogoffset+2) * 2, dialogoffset + 2)\
2039 \
2040 term.setCursorPos(dialogoffset, 3)\
2041 term.setBackgroundColour(colours.grey)\
2042 term.setTextColour(colours.lightGrey)\
2043 term.write(string.rep(\" \", w - dialogoffset * 2))\
2044 term.setCursorPos(dialogoffset + (w - dialogoffset * 2)/2 - #ctitle/2, 3)\
2045 term.write(ctitle)\
2046 term.setTextColour(colours.grey)\
2047 term.setBackgroundColour(colours.lightGrey)\
2048 term.setCursorPos(dialogoffset, 4)\
2049 term.write(string.rep(\" \", w - dialogoffset * 2))\
2050 for i=5,5+lines do\
2051 term.setCursorPos(dialogoffset, i)\
2052 term.write(\" \"..string.rep(\" \", w - (dialogoffset) * 2 - 2)..\" \")\
2053 end\
2054 wprintOffCenter(msg, 5, w - (dialogoffset+2) * 2, dialogoffset + 2)\
2055 \
2056 --In the event of a message, the player hits anything to continue\
2057 while true do\
2058 local id,key = os.pullEvent()\
2059 if id == \"timer\" then updateTimer(key);\
2060 elseif id == \"key\" or id == \"mouse_click\" or id == \"mouse_drag\" then break end\
2061 end\
2062end\
2063 \
2064--[[Produces a nice dropdown menu based on a table of strings. Depending on the position, this will auto-adjust the position\
2065 of the menu drawn, and allows nesting of menus and sub menus. Clicking anywhere outside the menu will cancel and return nothing\
2066 Params: x:int = the x position the menu should be displayed at\
2067 y:int = the y position the menu should be displayed at\
2068 options:table = the list of options available to the user, as strings or submenus (tables of strings, with a name parameter)\
2069 Returns:string the selected menu option.\
2070]]--\
2071local function displayDropDown(x, y, options)\
2072 inDropDown = true\
2073 --Figures out the dimensions of our thing\
2074 local longestX = #options.name\
2075 for i=1,#options do\
2076 local currVal = options[i]\
2077 if type(currVal) == \"table\" then currVal = currVal.name end\
2078 \
2079 longestX = math.max(longestX, #currVal)\
2080 end\
2081 local xOffset = math.max(0, longestX - ((w-2) - x) + 1)\
2082 local yOffset = math.max(0, #options - ((h-1) - y))\
2083 \
2084 local clickTimes = 0\
2085 local tid = nil\
2086 local selection = nil\
2087 while clickTimes < 2 do\
2088 drawCanvas()\
2089 drawInterface()\
2090 \
2091 term.setCursorPos(x-xOffset,y-yOffset)\
2092 term.setBackgroundColour(colours.grey)\
2093 term.setTextColour(colours.lightGrey)\
2094 term.write(options.name..string.rep(\" \", longestX-#options.name + 2))\
2095 \
2096 for i=1,#options do\
2097 term.setCursorPos(x-xOffset, y-yOffset+i)\
2098 if i==selection and clickTimes % 2 == 0 then\
2099 term.setBackgroundColour(colours.grey)\
2100 term.setTextColour(colours.lightGrey)\
2101 else\
2102 term.setBackgroundColour(colours.lightGrey)\
2103 term.setTextColour(colours.grey)\
2104 end\
2105 local currVal = options[i]\
2106 if type(currVal) == \"table\" then\
2107 term.write(currVal.name..string.rep(\" \", longestX-#currVal.name + 1))\
2108 term.setBackgroundColour(colours.grey)\
2109 term.setTextColour(colours.lightGrey)\
2110 term.write(\">\")\
2111 else\
2112 term.write(currVal..string.rep(\" \", longestX-#currVal + 2))\
2113 end\
2114 end\
2115 \
2116 local id, p1, p2, p3 = os.pullEvent()\
2117 if id == \"timer\" then\
2118 if p1 == tid then\
2119 clickTimes = clickTimes + 1\
2120 if clickTimes > 2 then\
2121 break\
2122 else\
2123 tid = os.startTimer(0.1)\
2124 end\
2125 else\
2126 updateTimer(p1)\
2127 drawCanvas()\
2128 drawInterface()\
2129 end\
2130 elseif id == \"mouse_click\" then\
2131 if p2 >=x-xOffset and p2 <= x-xOffset + longestX + 1 and p3 >= y-yOffset+1 and p3 <= y-yOffset+#options then\
2132 selection = p3-(y-yOffset)\
2133 tid = os.startTimer(0.1)\
2134 else\
2135 selection = \"\"\
2136 break\
2137 end\
2138 end\
2139 end\
2140 \
2141 if type(selection) == \"number\" then\
2142 selection = options[selection]\
2143 end\
2144 \
2145 if type(selection) == \"string\" then\
2146 inDropDown = false\
2147 return selection\
2148 elseif type(selection) == \"table\" then\
2149 return displayDropDown(x, y, selection)\
2150 end\
2151end\
2152 \
2153--[[A custom io.read() function with a few differences- it limits the number of characters being printed,\
2154 waits a 1/100th of a second so any keys still in the event library are removed before input is read and\
2155 the timer for the selectionrectangle is continuously updated during the process.\
2156 Params: lim:int = the number of characters input is allowed\
2157 Returns:string the inputted string, trimmed of leading and tailing whitespace\
2158]]--\
2159local function readInput(lim)\
2160 term.setCursorBlink(true)\
2161 \
2162 local inputString = \"\"\
2163 if not lim or type(lim) ~= \"number\" or lim < 1 then lim = w - ox end\
2164 local ox,oy = term.getCursorPos()\
2165 --We only get input from the footer, so this is safe. Change if recycling\
2166 term.setBackgroundColour(colours.lightGrey)\
2167 term.setTextColour(colours.grey)\
2168 term.write(string.rep(\" \", lim))\
2169 term.setCursorPos(ox, oy)\
2170 --As events queue immediately, we may get an unwanted key... this will solve that problem\
2171 local inputTimer = os.startTimer(0.01)\
2172 local keysAllowed = false\
2173 \
2174 while true do\
2175 local id,key = os.pullEvent()\
2176 \
2177 if keysAllowed then\
2178 if id == \"key\" and key == 14 and #inputString > 0 then\
2179 inputString = string.sub(inputString, 1, #inputString-1)\
2180 term.setCursorPos(ox + #inputString,oy)\
2181 term.write(\" \")\
2182 elseif id == \"key\" and key == 28 and inputString ~= string.rep(\" \", #inputString) then\
2183 break\
2184 elseif id == \"key\" and key == keys.leftCtrl then\
2185 return \"\"\
2186 elseif id == \"char\" and #inputString < lim then\
2187 inputString = inputString..key\
2188 end\
2189 end\
2190 \
2191 if id == \"timer\" then\
2192 if key == inputTimer then\
2193 keysAllowed = true\
2194 else\
2195 updateTimer(key)\
2196 drawCanvas()\
2197 drawInterface()\
2198 term.setBackgroundColour(colours.lightGrey)\
2199 term.setTextColour(colours.grey)\
2200 end\
2201 end\
2202 term.setCursorPos(ox,oy)\
2203 term.write(inputString)\
2204 term.setCursorPos(ox + #inputString, oy)\
2205 end\
2206 \
2207 while string.sub(inputString, 1, 1) == \" \" do\
2208 inputString = string.sub(inputString, 2, #inputString)\
2209 end\
2210 while string.sub(inputString, #inputString, #inputString) == \" \" do\
2211 inputString = string.sub(inputString, 1, #inputString-1)\
2212 end\
2213 term.setCursorBlink(false)\
2214 \
2215 return inputString\
2216end\
2217 \
2218--[[ \
2219 Section: Image tools\
2220]]--\
2221 \
2222 \
2223--[[Copies all pixels beneath the selection rectangle into the image buffer. Empty buffers are converted to nil.\
2224 Params: removeImage:bool = true if the image is to be erased after copying, false otherwise\
2225 Returns:nil\
2226]]--\
2227local function copyToBuffer(removeImage)\
2228 buffer = { width = selectrect.x2 - selectrect.x1 + 1, height = selectrect.y2 - selectrect.y1 + 1, contents = { } }\
2229 \
2230 local containsSomething = false\
2231 for y=1,buffer.height do\
2232 buffer.contents[y] = { }\
2233 local f,l = sFrame,sFrame\
2234 if record then f,l = 1, framecount end\
2235 \
2236 for fra = f,l do\
2237 if frames[fra][selectrect.y1 + y - 1] then\
2238 for x=1,buffer.width do\
2239 buffer.contents[y][x] = frames[sFrame][selectrect.y1 + y - 1][selectrect.x1 + x - 1]\
2240 if removeImage then frames[fra][selectrect.y1 + y - 1][selectrect.x1 + x - 1] = nil end\
2241 if buffer.contents[y][x] then containsSomething = true end\
2242 end\
2243 end\
2244 end\
2245 end\
2246 --I don't classify an empty buffer as a real buffer- confusing to the user.\
2247 if not containsSomething then buffer = nil end\
2248end\
2249 \
2250--[[Replaces all pixels under the selection rectangle with the image buffer (or what can be seen of it). Record-dependent.\
2251 Params: removeBuffer:bool = true if the buffer is to be emptied after copying, false otherwise\
2252 Returns:nil\
2253]]--\
2254local function copyFromBuffer(removeBuffer)\
2255 if not buffer then return end\
2256 \
2257 for y = 1, math.min(buffer.height,selectrect.y2-selectrect.y1+1) do\
2258 local f,l = sFrame, sFrame\
2259 if record then f,l = 1, framecount end\
2260 \
2261 for fra = f,l do\
2262 if not frames[fra][selectrect.y1+y-1] then frames[fra][selectrect.y1+y-1] = { } end\
2263 for x = 1, math.min(buffer.width,selectrect.x2-selectrect.x1+1) do\
2264 frames[fra][selectrect.y1+y-1][selectrect.x1+x-1] = buffer.contents[y][x]\
2265 end\
2266 end\
2267 end\
2268 \
2269 if removeBuffer then buffer = nil end\
2270end\
2271 \
2272--[[Moves the entire image (or entire animation) to the specified coordinates. Record-dependent.\
2273 Params: newx:int = the X coordinate to move the image to\
2274 newy:int = the Y coordinate to move the image to\
2275 Returns:nil\
2276]]--\
2277local function moveImage(newx,newy)\
2278 if not leflim or not toplim then return end\
2279 if newx <=0 or newy <=0 then return end\
2280 local f,l = sFrame,sFrame\
2281 if record then f,l = 1,framecount end\
2282 \
2283 for i=f,l do\
2284 local newlines = { }\
2285 for y=toplim,botlim do\
2286 local line = frames[i][y]\
2287 if line then\
2288 newlines[y-toplim+newy] = { }\
2289 for x,char in pairs(line) do\
2290 newlines[y-toplim+newy][x-leflim+newx] = char\
2291 end\
2292 end\
2293 end\
2294 --Exceptions that allow us to move the text as well\
2295 if textEnabled then\
2296 newlines.text = { }\
2297 for y=toplim,botlim do\
2298 local line = frames[i].text[y]\
2299 if line then\
2300 newlines.text[y-toplim+newy] = { }\
2301 for x,char in pairs(line) do\
2302 newlines.text[y-toplim+newy][x-leflim+newx] = char\
2303 end\
2304 end\
2305 end\
2306 \
2307 newlines.textcol = { }\
2308 for y=toplim,botlim do\
2309 local line = frames[i].textcol[y]\
2310 if line then\
2311 newlines.textcol[y-toplim+newy] = { }\
2312 for x,char in pairs(line) do\
2313 newlines.textcol[y-toplim+newy][x-leflim+newx] = char\
2314 end\
2315 end\
2316 end\
2317 end\
2318 \
2319 frames[i] = newlines\
2320 end\
2321end\
2322 \
2323--[[Prompts the user to clear the current frame or all frames. Record-dependent.,\
2324 Params: none\
2325 Returns:nil\
2326]]--\
2327local function clearImage()\
2328 inMenu = true\
2329 if not animated then\
2330 drawMessage(\"Clear image? Y/N: \")\
2331 elseif record then\
2332 drawMessage(\"Clear ALL frames? Y/N: \")\
2333 else\
2334 drawMessage(\"Clear current frame? Y/N :\")\
2335 end\
2336 if string.find(string.upper(readInput(1)), \"Y\") then\
2337 local f,l = sFrame,sFrame\
2338 if record then f,l = 1,framecount end\
2339 \
2340 for i=f,l do\
2341 frames[i] = { }\
2342 end\
2343 end\
2344 inMenu = false\
2345end\
2346 \
2347--[[A recursively called method (watch out for big calls!) in which every pixel of a set colour is\
2348 changed to another colour. Does not work on the nil colour, for obvious reasons.\
2349 Params: x:int = The X coordinate of the colour to flood-fill\
2350 y:int = The Y coordinate of the colour to flood-fill\
2351 targetColour:colour = the colour that is being flood-filled\
2352 newColour:colour = the colour with which to replace the target colour\
2353 Returns:nil\
2354]]--\
2355local function floodFill(x, y, targetColour, newColour)\
2356 if not newColour or not targetColour then return end\
2357 local nodeList = { }\
2358 \
2359 table.insert(nodeList, {x = x, y = y})\
2360 \
2361 while #nodeList > 0 do\
2362 local node = nodeList[1]\
2363 if frames[sFrame][node.y] and frames[sFrame][node.y][node.x] == targetColour then\
2364 frames[sFrame][node.y][node.x] = newColour\
2365 table.insert(nodeList, { x = node.x + 1, y = node.y})\
2366 table.insert(nodeList, { x = node.x, y = node.y + 1})\
2367 if x > 1 then table.insert(nodeList, { x = node.x - 1, y = node.y}) end\
2368 if y > 1 then table.insert(nodeList, { x = node.x, y = node.y - 1}) end\
2369 end\
2370 table.remove(nodeList, 1)\
2371 end\
2372end\
2373 \
2374--[[ \
2375 Section: Animation Tools \
2376]]--\
2377 \
2378--[[Enters play mode, allowing the animation to play through. Interface is restricted to allow this,\
2379 and method only leaves once the player leaves play mode.\
2380 Params: none\
2381 Returns:nil\
2382]]--\
2383local function playAnimation()\
2384 state = \"play\"\
2385 selectedrect = nil\
2386 \
2387 local animt = os.startTimer(animtime)\
2388 repeat\
2389 drawCanvas()\
2390 drawInterface()\
2391 \
2392 local id,key,_,y = os.pullEvent()\
2393 \
2394 if id==\"timer\" then\
2395 if key == animt then\
2396 animt = os.startTimer(animtime)\
2397 sFrame = (sFrame % framecount) + 1\
2398 else\
2399 updateTimer(key)\
2400 end\
2401 elseif id==\"key\" then\
2402 if key == keys.comma and animtime > 0.1 then animtime = animtime - 0.05\
2403 elseif key == keys.period and animtime < 0.5 then animtime = animtime + 0.05\
2404 elseif key == keys.space then state = \"paint\" end\
2405 elseif id==\"mouse_click\" and y == h then\
2406 state = \"paint\"\
2407 end\
2408 until state ~= \"play\"\
2409 os.startTimer(0.5)\
2410end\
2411 \
2412--[[Changes the selected frame (sFrame) to the chosen frame. If this frame is above the framecount,\
2413 additional frames are created with a copy of the image on the selected frame.\
2414 Params: newframe:int = the new frame to move to\
2415 Returns:nil\
2416]]--\
2417local function changeFrame(newframe)\
2418 inMenu = true\
2419 if not tonumber(newframe) then\
2420 term.setCursorPos(1,h)\
2421 term.setBackgroundColour(colours.lightGrey)\
2422 term.setTextColour(colours.grey)\
2423 term.clearLine()\
2424 \
2425 term.write(\"Go to frame: \")\
2426 newframe = tonumber(readInput(2))\
2427 if not newframe or newframe <= 0 then\
2428 inMenu = false\
2429 return\
2430 end\
2431 elseif newframe <= 0 then return end\
2432 \
2433 if newframe > framecount then\
2434 for i=framecount+1,newframe do\
2435 frames[i] = {}\
2436 for y,line in pairs(frames[sFrame]) do\
2437 frames[i][y] = { }\
2438 for x,v in pairs(line) do\
2439 frames[i][y][x] = v\
2440 end\
2441 end\
2442 end\
2443 framecount = newframe\
2444 end\
2445 sFrame = newframe\
2446 inMenu = false\
2447end\
2448 \
2449--[[Removes every frame leading after the frame passed in\
2450 Params: frame:int the non-inclusive lower bounds of the delete\
2451 Returns:nil\
2452]]--\
2453local function removeFramesAfter(frame)\
2454 inMenu = true\
2455 if frame==framecount then return end\
2456 drawMessage(\"Remove frames \"..(frame+1)..\"/\"..framecount..\"? Y/N :\")\
2457 local answer = string.upper(readInput(1))\
2458 \
2459 if string.find(answer, string.upper(\"Y\")) ~= 1 then\
2460 inMenu = false\
2461 return\
2462 end\
2463 \
2464 for i=frame+1, framecount do\
2465 frames[i] = nil\
2466 end\
2467 framecount = frame\
2468 inMenu = false\
2469end\
2470 \
2471--[[\
2472 Section: Printing Tools\
2473]]--\
2474 \
2475--[[Constructs a new facing to the left of the current facing\
2476 Params: curx:number = The facing on the X axis\
2477 curz:number = The facing on the Z axis\
2478 hand:string = The hand of the axis (\"right\" or \"left\")\
2479 Returns:number,number = the new facing on the X and Z axis after a left turn\
2480]]--\
2481local function getLeft(curx, curz)\
2482 local hand = \"left\"\
2483 if layering == \"up\" then hand = \"right\" end\
2484 \
2485 if hand == \"right\" then\
2486 if curx == 1 then return 0,-1 end\
2487 if curx == -1 then return 0,1 end\
2488 if curz == 1 then return 1,0 end\
2489 if curz == -1 then return -1,0 end\
2490 else\
2491 if curx == 1 then return 0,1 end\
2492 if curx == -1 then return 0,-1 end\
2493 if curz == 1 then return -1,0 end\
2494 if curz == -1 then return 1,0 end\
2495 end\
2496end\
2497 \
2498--[[Constructs a new facing to the right of the current facing\
2499 Params: curx:number = The facing on the X axis\
2500 curz:number = The facing on the Z axis\
2501 hand:string = The hand of the axis (\"right\" or \"left\")\
2502 Returns:number,number = the new facing on the X and Z axis after a right turn\
2503]]--\
2504local function getRight(curx, curz)\
2505 local hand = \"left\"\
2506 if layering == \"up\" then hand = \"right\" end\
2507 \
2508 if hand == \"right\" then\
2509 if curx == 1 then return 0,1 end\
2510 if curx == -1 then return 0,-1 end\
2511 if curz == 1 then return -1,0 end\
2512 if curz == -1 then return 1,0 end\
2513 else\
2514 if curx == 1 then return 0,-1 end\
2515 if curx == -1 then return 0,1 end\
2516 if curz == 1 then return 1,0 end\
2517 if curz == -1 then return -1,0 end\
2518 end\
2519end\
2520 \
2521 \
2522--[[Sends out a rednet signal requesting local printers, and will listen for any responses. Printers found are added to the\
2523 printerList (for ID's) and printerNames (for names)\
2524 Params: nil\
2525 Returns:nil\
2526]]--\
2527local function locatePrinters()\
2528 printerList = { }\
2529 printerNames = { name = \"Printers\" }\
2530 local oldState = state\
2531 state = \"Locating printers, please wait... \"\
2532 drawCanvas()\
2533 drawInterface()\
2534 state = oldState\
2535 \
2536 local modemOpened = false\
2537 for k,v in pairs(rs.getSides()) do\
2538 if peripheral.isPresent(v) and peripheral.getType(v) == \"modem\" then\
2539 rednet.open(v)\
2540 modemOpened = true\
2541 break\
2542 end\
2543 end\
2544 \
2545 if not modemOpened then\
2546 displayConfirmDialogue(\"Modem not found!\", \"No modem peripheral. Must have network modem to locate printers.\")\
2547 return false\
2548 end\
2549 \
2550 rednet.broadcast(\"$3DPRINT IDENTIFY\")\
2551 \
2552 while true do\
2553 local id, msg = rsTimeReceive(1)\
2554 \
2555 if not id then break end\
2556 if string.find(msg, \"$3DPRINT IDACK\") == 1 then\
2557 msg = string.gsub(msg, \"$3DPRINT IDACK \", \"\")\
2558 table.insert(printerList, id)\
2559 table.insert(printerNames, msg)\
2560 end\
2561 end\
2562 \
2563 if #printerList == 0 then\
2564 displayConfirmDialogue(\"Printers not found!\", \"No active printers found in proximity of this computer.\")\
2565 return false\
2566 else\
2567 return true\
2568 end\
2569end\
2570 \
2571--[[Sends a request to the printer. Waits on a response and updates the state of the application accordingly.\
2572 Params: command:string the command to send\
2573 param:string a parameter to send, if any\
2574 Returns:nil\
2575]]--\
2576local function sendPC(command,param)\
2577 local msg = \"$PC \"..command\
2578 if param then msg = msg..\" \"..param end\
2579 rednet.send(printerList[selectedPrinter], msg)\
2580 \
2581 while true do\
2582 local id,key = rsTimeReceive()\
2583 if id == printerList[selectedPrinter] then\
2584 if key == \"$3DPRINT ACK\" then\
2585 break\
2586 elseif key == \"$3DPRINT DEP\" then\
2587 displayConfirmDialogue(\"Printer Empty\", \"The printer has exhasted a material. Please refill slot \"..param..\
2588 \", and click this message when ready to continue.\")\
2589 rednet.send(printerList[selectedPrinter], msg)\
2590 elseif key == \"$3DPRINT OOF\" then\
2591 displayConfirmDialogue(\"Printer Out of Fuel\", \"The printer has no fuel. Please replace the material \"..\
2592 \"in slot 1 with a fuel source, then click this message.\")\
2593 rednet.send(printerList[selectedPrinter], \"$PC SS 1\")\
2594 id,key = rsTimeReceive()\
2595 rednet.send(printerList[selectedPrinter], \"$PC RF\")\
2596 id,key = rsTimeReceive()\
2597 rednet.send(printerList[selectedPrinter], msg)\
2598 end\
2599 end\
2600 end\
2601 \
2602 --Changes to position are handled after the event has been successfully completed\
2603 if command == \"FW\" then\
2604 px = px + pfx\
2605 pz = pz + pfz\
2606 elseif command == \"BK\" then\
2607 px = px - pfx\
2608 pz = pz - pfz\
2609 elseif command == \"UP\" then\
2610 if layering == \"up\" then\
2611 py = py + 1\
2612 else\
2613 py = py - 1\
2614 end\
2615 elseif command == \"DW\" then\
2616 if layering == \"up\" then\
2617 py = py - 1\
2618 else \
2619 py = py + 1\
2620 end\
2621 elseif command == \"TL\" then\
2622 pfx,pfz = getLeft(pfx,pfz)\
2623 elseif command == \"TR\" then\
2624 pfx,pfz = getRight(pfx,pfz)\
2625 elseif command == \"TU\" then\
2626 pfx = -pfx\
2627 pfz = -pfz\
2628 end\
2629 \
2630 drawCanvas()\
2631 drawInterface()\
2632end\
2633 \
2634--[[A printing function that commands the printer to turn to face the desired direction, if it is not already doing so\
2635 Params: desx:number = the normalized x direction to face\
2636 desz:number = the normalized z direction to face\
2637 Returns:nil\
2638]]--\
2639local function turnToFace(desx,desz)\
2640 if desx ~= 0 then\
2641 if pfx ~= desx then\
2642 local temppfx,_ = getLeft(pfx,pfz)\
2643 if temppfx == desx then\
2644 sendPC(\"TL\")\
2645 elseif temppfx == -desx then\
2646 sendPC(\"TR\")\
2647 else\
2648 sendPC(\"TU\")\
2649 end\
2650 end\
2651 else\
2652 print(\"on the z axis\")\
2653 if pfz ~= desz then\
2654 local _,temppfz = getLeft(pfx,pfz)\
2655 if temppfz == desz then\
2656 sendPC(\"TL\")\
2657 elseif temppfz == -desz then\
2658 sendPC(\"TR\")\
2659 else\
2660 sendPC(\"TU\")\
2661 end\
2662 end\
2663 end\
2664end\
2665 \
2666--[[Performs the print\
2667 Params: nil\
2668 Returns:nil\
2669]]--\
2670local function performPrint()\
2671 state = \"active print\"\
2672 if layering == \"up\" then\
2673 --An up layering starts our builder bot on the bottom left corner of our build\
2674 px,py,pz = leflim, 0, botlim + 1\
2675 pfx,pfz = 0,-1\
2676 \
2677 --We move him forward and up a bit from his original position.\
2678 sendPC(\"FW\")\
2679 sendPC(\"UP\")\
2680 --For each layer that needs to be completed, we go up by one each time\
2681 for layers=1,#frames do\
2682 --We first decide if we're going forwards or back, depending on what side we're on\
2683 local rowbot,rowtop,rowinc = nil,nil,nil\
2684 if pz == botlim then\
2685 rowbot,rowtop,rowinc = botlim,toplim,-1\
2686 else\
2687 rowbot,rowtop,rowinc = toplim,botlim,1\
2688 end\
2689 \
2690 for rows = rowbot,rowtop,rowinc do\
2691 --Then we decide if we're going left or right, depending on what side we're on\
2692 local linebot,linetop,lineinc = nil,nil,nil\
2693 if px == leflim then\
2694 --Facing from the left side has to be easterly- it's changed here\
2695 turnToFace(1,0)\
2696 linebot,linetop,lineinc = leflim,riglim,1\
2697 else\
2698 --Facing from the right side has to be westerly- it's changed here\
2699 turnToFace(-1,0)\
2700 linebot,linetop,lineinc = riglim,leflim,-1\
2701 end\
2702 \
2703 for lines = linebot,linetop,lineinc do\
2704 --We move our turtle forward, placing the right material at each step\
2705 local material = frames[py][pz][px]\
2706 if material then\
2707 material = math.log10(frames[py][pz][px])/math.log10(2) + 1\
2708 sendPC(\"SS\", material)\
2709 sendPC(\"PD\")\
2710 end\
2711 if lines ~= linetop then\
2712 sendPC(\"FW\")\
2713 end\
2714 end\
2715 \
2716 --The printer then has to do a U-turn, depending on which way he's facing and\
2717 --which way he needs to go\
2718 local temppfx,temppfz = getLeft(pfx,pfz)\
2719 if temppfz == rowinc and rows ~= rowtop then\
2720 sendPC(\"TL\")\
2721 sendPC(\"FW\")\
2722 sendPC(\"TL\")\
2723 elseif temppfz == -rowinc and rows ~= rowtop then\
2724 sendPC(\"TR\")\
2725 sendPC(\"FW\")\
2726 sendPC(\"TR\")\
2727 end\
2728 end\
2729 --Now at the end of a run he does a 180 and moves up to begin the next part of the print\
2730 sendPC(\"TU\")\
2731 if layers ~= #frames then\
2732 sendPC(\"UP\")\
2733 end\
2734 end\
2735 --All done- now we head back to where we started.\
2736 if px ~= leflim then\
2737 turnToFace(-1,0)\
2738 while px ~= leflim do\
2739 sendPC(\"FW\")\
2740 end\
2741 end\
2742 if pz ~= botlim then\
2743 turnToFace(0,-1)\
2744 while pz ~= botlim do\
2745 sendPC(\"BK\")\
2746 end\
2747 end\
2748 turnToFace(0,-1)\
2749 sendPC(\"BK\")\
2750 while py > 0 do\
2751 sendPC(\"DW\")\
2752 end\
2753 else\
2754 --The front facing is at the top-left corner, facing south not north\
2755 px,py,pz = leflim, botlim, 1\
2756 pfx,pfz = 0,1\
2757 --We move the printer to the last layer- he prints from the back forwards\
2758 while pz < #frames do\
2759 sendPC(\"FW\")\
2760 end\
2761 \
2762 --For each layer in the frame we build our wall, the move back\
2763 for layers = 1,#frames do\
2764 --We first decide if we're going left or right based on our position\
2765 local rowbot,rowtop,rowinc = nil,nil,nil\
2766 if px == leflim then\
2767 rowbot,rowtop,rowinc = leflim,riglim,1\
2768 else\
2769 rowbot,rowtop,rowinc = riglim,leflim,-1\
2770 end\
2771 \
2772 for rows = rowbot,rowtop,rowinc do\
2773 --Then we decide if we're going up or down, depending on our given altitude\
2774 local linebot,linetop,lineinc = nil,nil,nil\
2775 if py == botlim then\
2776 linebot,linetop,lineinc = botlim,toplim,-1\
2777 else\
2778 linebot,linetop,lineinc = toplim,botlim,1\
2779 end\
2780 \
2781 for lines = linebot,linetop,lineinc do\
2782 --We move our turtle up/down, placing the right material at each step\
2783 local material = frames[pz][py][px]\
2784 if material then\
2785 material = math.log10(frames[pz][py][px])/math.log10(2) + 1\
2786 sendPC(\"SS\", material)\
2787 sendPC(\"PF\")\
2788 end\
2789 if lines ~= linetop then\
2790 if lineinc == 1 then sendPC(\"DW\")\
2791 else sendPC(\"UP\") end\
2792 end\
2793 end\
2794 \
2795 if rows ~= rowtop then\
2796 turnToFace(rowinc,0)\
2797 sendPC(\"FW\")\
2798 turnToFace(0,1)\
2799 end\
2800 end\
2801 \
2802 if layers ~= #frames then\
2803 sendPC(\"TU\")\
2804 sendPC(\"FW\")\
2805 sendPC(\"TU\")\
2806 end\
2807 end\
2808 --He's easy to reset\
2809 while px ~= leflim do\
2810 turnToFace(-1,0)\
2811 sendPC(\"FW\")\
2812 end\
2813 turnToFace(0,1)\
2814 end\
2815 \
2816 sendPC(\"DE\")\
2817 \
2818 displayConfirmDialogue(\"Print complete\", \"The 3D print was successful.\")\
2819end\
2820 \
2821--[[ \
2822 Section: Interface \
2823]]--\
2824 \
2825--[[Runs the printing interface. Allows users to find/select a printer, the style of printing to perform and to begin the operation\
2826 Params: none\
2827 Returns:boolean true if printing was started, false otherwse\
2828]]--\
2829local function runPrintInterface()\
2830 calculateMaterials()\
2831 --There's nothing on canvas yet!\
2832 if not botlim then\
2833 displayConfirmDialogue(\"Cannot Print Empty Canvas\", \"There is nothing on canvas that \"..\
2834 \"can be printed, and the operation cannot be completed.\")\
2835 return false\
2836 end\
2837 --No printers nearby\
2838 if not locatePrinters() then\
2839 return false\
2840 end\
2841 \
2842 layering = \"up\"\
2843 requirementsDisplayed = false\
2844 selectedPrinter = 1\
2845 while true do\
2846 drawCanvas()\
2847 term.setBackgroundColour(colours.lightGrey)\
2848 for i=1,10 do\
2849 term.setCursorPos(1,i)\
2850 term.clearLine()\
2851 end\
2852 drawInterface()\
2853 term.setBackgroundColour(colours.lightGrey)\
2854 term.setTextColour(colours.black)\
2855 \
2856 local msg = \"3D Printing\"\
2857 term.setCursorPos(w/2-#msg/2 - 2, 1)\
2858 term.write(msg)\
2859 term.setBackgroundColour(colours.grey)\
2860 term.setTextColour(colours.lightGrey)\
2861 if(requirementsDisplayed) then\
2862 msg = \"Count:\"\
2863 else\
2864 msg = \" Slot:\"\
2865 end\
2866 term.setCursorPos(w-3-#msg, 1)\
2867 term.write(msg)\
2868 term.setBackgroundColour(colours.lightGrey)\
2869 term.setTextColour(colours.black)\
2870 \
2871 term.setCursorPos(7, 2)\
2872 term.write(\"Layering\")\
2873 drawPictureTable(layerUpIcon, 3, 3, colours.white)\
2874 drawPictureTable(layerForwardIcon, 12, 3, colours.white)\
2875 if layering == \"up\" then\
2876 term.setBackgroundColour(colours.red)\
2877 else\
2878 term.setBackgroundColour(colours.lightGrey)\
2879 end\
2880 term.setCursorPos(3, 9)\
2881 term.write(\"Upwards\")\
2882 if layering == \"forward\" then\
2883 term.setBackgroundColour(colours.red)\
2884 else\
2885 term.setBackgroundColour(colours.lightGrey)\
2886 end\
2887 term.setCursorPos(12, 9)\
2888 term.write(\"Forward\")\
2889 \
2890 term.setBackgroundColour(colours.lightGrey)\
2891 term.setTextColour(colours.black)\
2892 term.setCursorPos(31, 2)\
2893 term.write(\"Printer ID\")\
2894 term.setCursorPos(33, 3)\
2895 if #printerList > 1 then\
2896 term.setBackgroundColour(colours.grey)\
2897 term.setTextColour(colours.lightGrey)\
2898 else\
2899 term.setTextColour(colours.red)\
2900 end\
2901 term.write(\" \"..printerNames[selectedPrinter]..\" \")\
2902 \
2903 term.setBackgroundColour(colours.grey)\
2904 term.setTextColour(colours.lightGrey)\
2905 term.setCursorPos(25, 10)\
2906 term.write(\" Cancel \")\
2907 term.setCursorPos(40, 10)\
2908 term.write(\" Print \")\
2909 \
2910 local id, p1, p2, p3 = os.pullEvent()\
2911 \
2912 if id == \"timer\" then\
2913 updateTimer(p1)\
2914 elseif id == \"mouse_click\" then\
2915 --Layering Buttons\
2916 if p2 >= 3 and p2 <= 9 and p3 >= 3 and p3 <= 9 then\
2917 layering = \"up\"\
2918 elseif p2 >= 12 and p2 <= 18 and p3 >= 3 and p3 <= 9 then\
2919 layering = \"forward\"\
2920 --Count/Slot\
2921 elseif p2 >= w - #msg - 3 and p2 <= w - 3 and p3 == 1 then\
2922 requirementsDisplayed = not requirementsDisplayed\
2923 --Printer ID\
2924 elseif p2 >= 33 and p2 <= 33 + #printerNames[selectedPrinter] and p3 == 3 and #printerList > 1 then\
2925 local chosenName = displayDropDown(33, 3, printerNames)\
2926 for i=1,#printerNames do\
2927 if printerNames[i] == chosenName then\
2928 selectedPrinter = i\
2929 break;\
2930 end\
2931 end\
2932 --Print and Cancel\
2933 elseif p2 >= 25 and p2 <= 32 and p3 == 10 then\
2934 break\
2935 elseif p2 >= 40 and p2 <= 46 and p3 == 10 then\
2936 rednet.send(printerList[selectedPrinter], \"$3DPRINT ACTIVATE\")\
2937 ready = false\
2938 while true do\
2939 local id,msg = rsTimeReceive(10)\
2940 \
2941 if id == printerList[selectedPrinter] and msg == \"$3DPRINT ACTACK\" then\
2942 ready = true\
2943 break\
2944 end\
2945 end\
2946 if ready then\
2947 performPrint()\
2948 break\
2949 else\
2950 displayConfirmDialogue(\"Printer Didn't Respond\", \"The printer didn't respond to the activation command. Check to see if it's online\")\
2951 end\
2952 end\
2953 end\
2954 end\
2955 state = \"paint\"\
2956end\
2957\
2958--[[Performs a legacy save. When the dropdown menu is unavailable, it requests the user to save\
2959\009or exit using keyboard shortcuts rather than selecting a menu option from the dropdown.\
2960\009Pressing the control key again will cancel the save operation.\
2961\009Params: none\
2962\009Returns:string = the selection made\
2963]]--\
2964local function performLegacySaveExit()\
2965\009local saveMsg = \"(S)ave/(E)xit?\"\
2966\009if w < #saveMsg then saveMsg = \"S/E?\" end\
2967\009\
2968\009term.setCursorPos(1,h)\
2969\009term.setBackgroundColour(colours.lightGrey)\
2970\009term.setTextColour(colours.grey)\
2971\009term.clearLine()\
2972\009term.write(saveMsg)\
2973\009\
2974\009while true do\
2975\009\009local id,val = os.pullEvent()\
2976\009\009if id == \"timer\" then updateTimer(val)\
2977\009\009elseif id == \"key\" then\
2978\009\009\009if val == keys.s then return \"save\" \
2979\009\009\009elseif val == keys.e then\
2980\009\009\009\009--Get rid of the extra event\
2981\009\009\009\009os.pullEvent(\"char\")\
2982\009\009\009\009return \"exit\"\
2983\009\009\009elseif val == keys.leftCtrl then return nil\
2984\009\009\009end\
2985\009\009end\
2986\009end\
2987end\
2988 \
2989--[[This function changes the current paint program to another tool or mode, depending on user input. Handles\
2990 any necessary changes in logic involved in that.\
2991 Params: mode:string = the name of the mode to change to\
2992 Returns:nil\
2993]]--\
2994local function performSelection(mode)\
2995 if not mode or mode == \"\" then return\
2996 \
2997 elseif mode == \"help\" and helpAvailable then\
2998 drawHelpScreen()\
2999 \
3000 elseif mode == \"blueprint on\" then\
3001 blueprint = true\
3002\009\009\009\009for i=1,#ddModes[2] do if ddModes[2][i] == \"blueprint on\" then\
3003\009\009\009\009\009ddModes[2][i] = \"blueprint off\"\
3004\009\009\009\009end end\
3005 \
3006 elseif mode == \"blueprint off\" then\
3007 blueprint = false\
3008\009\009\009\009for i=1,#ddModes[2] do if ddModes[2][i] == \"blueprint off\" then\
3009\009\009\009\009\009ddModes[2][i] = \"blueprint on\"\
3010\009\009\009\009end end\
3011 \
3012 elseif mode == \"layers on\" then\
3013 layerDisplay = true\
3014\009\009\009\009for i=1,#ddModes[2] do if ddModes[2][i] == \"layers on\" then\
3015\009\009\009\009\009ddModes[2][i] = \"layers off\"\
3016\009\009\009\009end end\
3017 \
3018 elseif mode == \"layers off\" then\
3019 layerDisplay = false\
3020\009\009\009\009for i=1,#ddModes[2] do if ddModes[2][i] == \"layers off\" then\
3021\009\009\009\009\009ddModes[2][i] = \"layers on\"\
3022\009\009\009\009end end\
3023 \
3024 elseif mode == \"direction on\" then\
3025 printDirection = true\
3026\009\009\009\009for i=1,#ddModes[2] do if ddModes[2][i] == \"direction on\" then\
3027\009\009\009\009\009ddModes[2][i] = \"direction off\"\
3028\009\009\009\009end end\
3029 \
3030 elseif mode == \"direction off\" then\
3031 printDirection = false\
3032\009\009\009\009for i=1,#ddModes[2] do if ddModes[2][i] == \"direction off\" then\
3033\009\009\009\009\009ddModes[2][i] = \"direction on\"\
3034\009\009\009\009end end\
3035\009\009\
3036\009\009elseif mode == \"hide interface\" then\
3037\009\009\009\009interfaceHidden = true\
3038\009\009\009\
3039\009\009elseif mode == \"show interface\" then\
3040\009\009\009\009interfaceHidden = false\
3041 \
3042 elseif mode == \"go to\" then\
3043 changeFrame()\
3044 \
3045 elseif mode == \"remove\" then\
3046 removeFramesAfter(sFrame)\
3047 \
3048 elseif mode == \"play\" then\
3049 playAnimation()\
3050 \
3051 elseif mode == \"copy\" then\
3052 if selectrect and selectrect.x1 ~= selectrect.x2 then\
3053 copyToBuffer(false)\
3054 end\
3055 \
3056 elseif mode == \"cut\" then\
3057 if selectrect and selectrect.x1 ~= selectrect.x2 then\
3058 copyToBuffer(true)\
3059 end\
3060 \
3061 elseif mode == \"paste\" then\
3062 if selectrect and selectrect.x1 ~= selectrect.x2 then\
3063 copyFromBuffer(false)\
3064 end\
3065 \
3066 elseif mode == \"hide\" then\
3067 selectrect = nil\
3068 if state == \"select\" then state = \"corner select\" end\
3069\009\009\009 \
3070 elseif mode == \"alpha to left\" then\
3071 if lSel then alphaC = lSel end\
3072 \
3073 elseif mode == \"alpha to right\" then\
3074 if rSel then alphaC = rSel end\
3075 \
3076 elseif mode == \"record\" then\
3077 record = not record\
3078 \
3079 elseif mode == \"clear\" then\
3080 if state==\"select\" then buffer = nil\
3081 else clearImage() end\
3082 \
3083 elseif mode == \"select\" then\
3084 if state==\"corner select\" or state==\"select\" then\
3085 state = \"paint\"\
3086 elseif selectrect and selectrect.x1 ~= selectrect.x2 then\
3087 state = \"select\"\
3088 else\
3089 state = \"corner select\"\
3090 end\
3091 \
3092 elseif mode == \"print\" then\
3093 state = \"print\"\
3094 runPrintInterface()\
3095 state = \"paint\"\
3096 \
3097 elseif mode == \"save\" then\
3098 if animated then saveNFA(sPath)\
3099 elseif textEnabled then saveNFT(sPath)\
3100 else saveNFP(sPath) end\
3101 \
3102 elseif mode == \"exit\" then\
3103 isRunning = false\
3104 \
3105 elseif mode ~= state then state = mode\
3106 else state = \"paint\"\
3107 end\
3108end\
3109 \
3110--[[The main function of the program, reads and handles all events and updates them accordingly. Mode changes,\
3111 painting to the canvas and general selections are done here.\
3112 Params: none\
3113 Returns:nil\
3114]]--\
3115local function handleEvents()\
3116 recttimer = os.startTimer(0.5)\
3117 while isRunning do\
3118 drawCanvas()\
3119 if not interfaceHidden then drawInterface() end\
3120 \
3121 if state == \"text\" then\
3122 term.setCursorPos(textCurX - sx, textCurY - sy)\
3123 term.setCursorBlink(true)\
3124 end\
3125 \
3126 local id,p1,p2,p3 = os.pullEvent()\
3127 term.setCursorBlink(false)\
3128 if id==\"timer\" then\
3129 updateTimer(p1)\
3130 elseif (id==\"mouse_click\" or id==\"mouse_drag\") and not interfaceHidden then\
3131 if p2 >=w-1 and p3 < #column+1 then\
3132\009\009\009\009\009\009\009\009local off = 0\
3133\009\009\009\009\009\009\009\009local cansel = true\
3134\009\009\009\009\009\009\009\009if h < #column + 2 then\
3135\009\009\009\009\009\009\009\009\009if p3 == 1 then \
3136\009\009\009\009\009\009\009\009\009\009if columnoffset > 0 then columnoffset = columnoffset-1 end\
3137\009\009\009\009\009\009\009\009\009\009cansel = false\
3138\009\009\009\009\009\009\009\009\009elseif p3 == h-2 then\
3139\009\009\009\009\009\009\009\009\009\009if columnoffset < #column-(h-4)+1 then columnoffset = columnoffset+1 end\
3140\009\009\009\009\009\009\009\009\009\009cansel = false\
3141\009\009\009\009\009\009\009\009\009else\
3142\009\009\009\009\009\009\009\009\009\009off = columnoffset - 1\
3143\009\009\009\009\009\009\009\009\009end\
3144\009\009\009\009\009\009\009\009end\
3145\009\009\009\009\009\009\009\009--This rather handily accounts for the nil case (p3+off=#column+1)\
3146\009\009\009\009\009\009\009\009if p1==1 and cansel then lSel = column[p3+off]\
3147 elseif p1==2 and cansel then rSel = column[p3+off] end\
3148 elseif p2 >=w-1 and p3==#column+1 then\
3149 if p1==1 then lSel = nil\
3150 else rSel = nil end\
3151 elseif p2==w-1 and p3==h and animated then\
3152 changeFrame(sFrame-1)\
3153 elseif p2==w and p3==h and animated then\
3154 changeFrame(sFrame+1)\
3155 elseif p2 <= #ddModes.name + 2 and p3==h and mainAvailable then\
3156 local sel = displayDropDown(1, h-1, ddModes)\
3157 performSelection(sel)\
3158 elseif p2 < w-1 and p3 <= h-1 then\
3159 if state==\"pippette\" then\
3160 if p1==1 then\
3161 if frames[sFrame][p3+sy] and frames[sFrame][p3+sy][p2+sx] then\
3162 lSel = frames[sFrame][p3+sy][p2+sx]\
3163 end\
3164 elseif p1==2 then\
3165 if frames[sFrame][p3+sy] and frames[sFrame][p3+sy][p2+sx] then\
3166 rSel = frames[sFrame][p3+sy][p2+sx]\
3167 end\
3168 end\
3169 elseif state==\"move\" then\
3170 updateImageLims(record)\
3171 moveImage(p2,p3)\
3172 elseif state==\"flood\" then\
3173 if p1 == 1 and lSel and frames[sFrame][p3+sy] then\
3174 floodFill(p2,p3,frames[sFrame][p3+sy][p2+sx],lSel)\
3175 elseif p1 == 2 and rSel and frames[sFrame][p3+sy] then\
3176 floodFill(p2,p3,frames[sFrame][p3+sy][p2+sx],rSel)\
3177 end\
3178 elseif state==\"corner select\" then\
3179 if not selectrect then\
3180 selectrect = { x1=p2+sx, x2=p2+sx, y1=p3+sy, y2=p3+sy }\
3181 elseif selectrect.x1 ~= p2+sx and selectrect.y1 ~= p3+sy then\
3182 if p2+sx<selectrect.x1 then selectrect.x1 = p2+sx\
3183 else selectrect.x2 = p2+sx end\
3184 \
3185 if p3+sy<selectrect.y1 then selectrect.y1 = p3+sy\
3186 else selectrect.y2 = p3+sy end\
3187 \
3188 state = \"select\"\
3189 end\
3190 elseif state==\"textpaint\" then\
3191 local paintCol = lSel\
3192 if p1 == 2 then paintCol = rSel end\
3193 if frames[sFrame].textcol[p3+sy] then\
3194 frames[sFrame].textcol[p3+sy][p2+sx] = paintCol\
3195 end\
3196 elseif state==\"text\" then\
3197 textCurX = p2 + sx\
3198 textCurY = p3 + sy\
3199 elseif state==\"select\" then\
3200 if p1 == 1 then\
3201 local swidth = selectrect.x2 - selectrect.x1\
3202 local sheight = selectrect.y2 - selectrect.y1\
3203 \
3204 selectrect.x1 = p2 + sx\
3205 selectrect.y1 = p3 + sy\
3206 selectrect.x2 = p2 + swidth + sx\
3207 selectrect.y2 = p3 + sheight + sy\
3208 elseif p1 == 2 and p2 < w-2 and p3 < h-1 and boxdropAvailable then\
3209 inMenu = true\
3210 local sel = displayDropDown(p2, p3, srModes)\
3211 inMenu = false\
3212 performSelection(sel)\
3213 end\
3214 else\
3215 local f,l = sFrame,sFrame\
3216 if record then f,l = 1,framecount end\
3217 local bwidth = 0\
3218 if state == \"brush\" then bwidth = brushsize-1 end\
3219 \
3220 for i=f,l do\
3221 for x = math.max(1,p2+sx-bwidth),p2+sx+bwidth do\
3222 for y = math.max(1,p3+sy-bwidth), p3+sy+bwidth do\
3223 if math.abs(x - (p2+sx)) + math.abs(y - (p3+sy)) <= bwidth then\
3224 if not frames[i][y] then frames[i][y] = {} end\
3225 if p1==1 then frames[i][y][x] = lSel\
3226 else frames[i][y][x] = rSel end\
3227 \
3228 if textEnabled then\
3229 if not frames[i].text[y] then frames[i].text[y] = { } end\
3230 if not frames[i].textcol[y] then frames[i].textcol[y] = { } end\
3231 end\
3232 end\
3233 end\
3234 end\
3235 end\
3236 end\
3237 end\
3238 elseif id==\"char\" then\
3239 if state==\"text\" then\
3240 if not frames[sFrame][textCurY] then frames[sFrame][textCurY] = { } end\
3241 if not frames[sFrame].text[textCurY] then frames[sFrame].text[textCurY] = { } end\
3242 if not frames[sFrame].textcol[textCurY] then frames[sFrame].textcol[textCurY] = { } end\
3243 \
3244 if rSel then frames[sFrame][textCurY][textCurX] = rSel end\
3245 if lSel then\
3246 frames[sFrame].text[textCurY][textCurX] = p1\
3247 frames[sFrame].textcol[textCurY][textCurX] = lSel\
3248 else\
3249 frames[sFrame].text[textCurY][textCurX] = \" \"\
3250 frames[sFrame].textcol[textCurY][textCurX] = rSel\
3251 end\
3252 \
3253 textCurX = textCurX+1\
3254 if textCurX > w + sx - 2 then sx = textCurX - w + 2 end\
3255 elseif tonumber(p1) then\
3256 if state==\"brush\" and tonumber(p1) > 1 then\
3257 brushsize = tonumber(p1)\
3258 elseif animated and tonumber(p1) > 0 then\
3259 changeFrame(tonumber(p1))\
3260 end\
3261 end\
3262 elseif id==\"key\" then\
3263\009\009\009\009\009\009--All standard interface methods are locked when the interface is hidden\
3264\009\009\009\009\009\009if interfaceHidden then\
3265\009\009\009\009\009\009\009if p1==keys.grave then\
3266\009\009\009\009\009\009\009\009performSelection(\"show interface\") \
3267\009\009\009\009\009\009\009end\
3268 --Text needs special handlers (all other keyboard shortcuts are of course reserved for typing)\
3269 elseif state==\"text\" then\
3270 if p1==keys.backspace and textCurX > 1 then\
3271 textCurX = textCurX-1\
3272 if frames[sFrame].text[textCurY] then\
3273 frames[sFrame].text[textCurY][textCurX] = nil\
3274 frames[sFrame].textcol[textCurY][textCurX] = nil\
3275 end\
3276 if textCurX < sx then sx = textCurX end\
3277 elseif p1==keys.left and textCurX > 1 then\
3278 textCurX = textCurX-1\
3279 if textCurX-1 < sx then sx = textCurX-1 end\
3280 elseif p1==keys.right then\
3281 textCurX = textCurX+1\
3282 if textCurX > w + sx - 2 then sx = textCurX - w + 2 end\
3283 elseif p1==keys.up and textCurY > 1 then\
3284 textCurY = textCurY-1\
3285 if textCurY-1 < sy then sy = textCurY-1 end\
3286 elseif p1==keys.down then\
3287 textCurY = textCurY+1\
3288 if textCurY > h + sy - 1 then sy = textCurY - h + 1 end\
3289 end\
3290 \
3291 elseif p1==keys.leftCtrl then\
3292 local sel = nil\
3293\009\009\009\009\009\009\009\009if mainAvailable then \
3294\009\009\009\009\009\009\009\009\009sel = displayDropDown(1, h-1, ddModes[#ddModes])\
3295\009\009\009\009\009\009\009\009else sel = performLegacySaveExit() end\
3296 performSelection(sel)\
3297 elseif p1==keys.leftAlt then\
3298 local sel = displayDropDown(1, h-1, ddModes[1])\
3299 performSelection(sel)\
3300 elseif p1==keys.h then\
3301 performSelection(\"help\")\
3302 elseif p1==keys.x then\
3303 performSelection(\"cut\")\
3304 elseif p1==keys.c then\
3305 performSelection(\"copy\")\
3306 elseif p1==keys.v then\
3307 performSelection(\"paste\")\
3308 elseif p1==keys.z then\
3309 performSelection(\"clear\")\
3310 elseif p1==keys.s then\
3311 performSelection(\"select\")\
3312 elseif p1==keys.tab then\
3313 performSelection(\"hide\")\
3314 elseif p1==keys.q then\
3315 performSelection(\"alpha to left\")\
3316 elseif p1==keys.w then\
3317 performSelection(\"alpha to right\")\
3318 elseif p1==keys.f then\
3319 performSelection(\"flood\")\
3320 elseif p1==keys.b then\
3321 performSelection(\"brush\")\
3322 elseif p1==keys.m then\
3323 performSelection(\"move\")\
3324 elseif p1==keys.backslash and animated then\
3325 performSelection(\"record\")\
3326 elseif p1==keys.p then\
3327 performSelection(\"pippette\")\
3328 elseif p1==keys.g and animated then\
3329 performSelection(\"go to\")\
3330\009\009\009\009\009\009elseif p1==keys.grave then \
3331\009\009\009\009\009\009\009\009performSelection(\"hide interface\")\
3332 elseif p1==keys.period and animated then\
3333 changeFrame(sFrame+1)\
3334 elseif p1==keys.comma and animated then\
3335 changeFrame(sFrame-1)\
3336 elseif p1==keys.r and animated then\
3337 performSelection(\"remove\")\
3338 elseif p1==keys.space and animated then\
3339 performSelection(\"play\")\
3340 elseif p1==keys.t and textEnabled then\
3341 performSelection(\"text\")\
3342 sleep(0.01)\
3343 elseif p1==keys.y and textEnabled then\
3344 performSelection(\"textpaint\")\
3345 elseif p1==keys.left then\
3346 if state == \"move\" and toplim then\
3347 updateImageLims(record)\
3348 if toplim and leflim then\
3349 moveImage(leflim-1,toplim)\
3350 end\
3351 elseif state==\"select\" and selectrect.x1 > 1 then\
3352 selectrect.x1 = selectrect.x1-1\
3353 selectrect.x2 = selectrect.x2-1\
3354 elseif sx > 0 then sx=sx-1 end\
3355 elseif p1==keys.right then\
3356 if state == \"move\" then\
3357 updateImageLims(record)\
3358 if toplim and leflim then\
3359 moveImage(leflim+1,toplim)\
3360 end\
3361 elseif state==\"select\" then\
3362 selectrect.x1 = selectrect.x1+1\
3363 selectrect.x2 = selectrect.x2+1\
3364 else sx=sx+1 end\
3365 elseif p1==keys.up then\
3366 if state == \"move\" then\
3367 updateImageLims(record)\
3368 if toplim and leflim then\
3369 moveImage(leflim,toplim-1)\
3370 end\
3371 elseif state==\"select\" and selectrect.y1 > 1 then\
3372 selectrect.y1 = selectrect.y1-1\
3373 selectrect.y2 = selectrect.y2-1\
3374 elseif sy > 0 then sy=sy-1 end\
3375 elseif p1==keys.down then\
3376 if state == \"move\" then\
3377 updateImageLims(record)\
3378 if toplim and leflim then\
3379 moveImage(leflim,toplim+1)\
3380 end\
3381 elseif state==\"select\" then\
3382 selectrect.y1 = selectrect.y1+1\
3383 selectrect.y2 = selectrect.y2+1\
3384 else sy=sy+1 end\
3385 end\
3386 end\
3387 end\
3388end\
3389 \
3390--[[\
3391 Section: Main \
3392]]--\
3393 \
3394--The first thing done is deciding what features we actually have, given the screen size\
3395if w < 7 or h < 4 then\
3396\009--NPaintPro simply doesn't work at certain configurations\
3397\009shell.run(\"clear\")\
3398\009print(\"Screen too small\")\
3399\009os.pullEvent(\"key\")\
3400\009return\
3401end\
3402--And reduces the number of features in others.\
3403determineAvailableServices()\
3404 \
3405--There is no b&w support for NPP.\
3406if not term.isColour() then\
3407\009shell.run(\"clear\")\
3408\009print(\"NPaintPro\\nBy NitrogenFingers\\n\\nNPaintPro can only be run on advanced \"..\
3409\009\"computers. Please reinstall on an advanced computer.\")\
3410 return\
3411end\
3412 \
3413--Taken almost directly from edit (for consistency)\
3414local tArgs = {...}\
3415 \
3416--Command line options can appear before the file path to specify the file format\
3417local ca = 1\
3418while ca <= #tArgs do\
3419\009if tArgs[ca] == \"-a\" then animated = true\
3420\009elseif tArgs[ca] == \"-t\" then textEnabled = true\
3421\009elseif tArgs[ca] == \"-d\" then interfaceHidden = true\
3422\009elseif string.sub(tArgs[ca], 1, 1) == \"-\" then\
3423\009\009print(\"Unrecognized option: \"..tArgs[ca])\
3424\009\009return\
3425\009else break end\
3426\009ca = ca + 1\
3427end\
3428\
3429--Presently, animations and text files are not supported\
3430if animated and textEnabled then\
3431 print(\"No support for animated text files- cannot have both -a and -t\")\
3432\009return\
3433end\
3434\
3435--Filepaths must be added if the screen is too small\
3436if #tArgs < ca then\
3437\009if not filemakerAvailable then\
3438\009\009print(\"Usage: npaintpro [-a,-t,-d] <path>\")\
3439\009\009return\
3440\009else\
3441\009\009--Otherwise do the logo draw early, to determine the file.\
3442\009\009drawLogo()\
3443\009\009if not runFileMaker() then return end\
3444\009end\
3445else\
3446\009if not interfaceHidden then drawLogo() end\
3447\009sPath = shell.resolve(tArgs[ca])\
3448end\
3449 \
3450if fs.exists(sPath) then\
3451 if fs.isDir(sPath) then\
3452 print(\"Cannot edit a directory.\")\
3453 return\
3454 elseif string.find(sPath, \".nfp\") ~= #sPath-3 and string.find(sPath, \".nfa\") ~= #sPath-3 and\
3455 string.find(sPath, \".nft\") ~= #sPath-3 then\
3456 print(\"Can only edit .nfp, .nft and .nfa files:\",string.find(sPath, \".nfp\"),#sPath-3)\
3457 return\
3458 end\
3459 \
3460 if string.find(sPath, \".nfa\") == #sPath-3 then\
3461 animated = true\
3462 end\
3463 \
3464 if string.find(sPath, \".nft\") == #sPath-3 then\
3465 textEnabled = true\
3466 end \
3467 \
3468 if string.find(sPath, \".nfp\") == #sPath-3 and animated then\
3469 print(\"Convert to nfa? Y/N\")\
3470 if string.find(string.lower(io.read()), \"y\") then\
3471 local nsPath = string.sub(sPath, 1, #sPath-1)..\"a\"\
3472 fs.move(sPath, nsPath)\
3473 sPath = nsPath\
3474 else\
3475 animated = false\
3476 end\
3477 end\
3478 \
3479 --Again this is possible, I just haven't done it. Maybe I will?\
3480 if textEnabled and (string.find(sPath, \".nfp\") == #sPath-3 or string.find(sPath, \".nfa\") == #sPath-3) then\
3481 print(\"Cannot convert to nft\")\
3482 end\
3483else\
3484 if not animated and not textEnabled and string.find(sPath, \".nfp\") ~= #sPath-3 then\
3485 sPath = sPath..\".nfp\"\
3486 elseif animated and string.find(sPath, \".nfa\") ~= #sPath-3 then\
3487 sPath = sPath..\".nfa\"\
3488 elseif textEnabled and string.find(sPath, \".nft\") ~= #sPath-3 then\
3489 sPath = sPath..\".nft\"\
3490 end\
3491end\
3492 \
3493init()\
3494handleEvents()\
3495 \
3496term.setBackgroundColour(colours.black)\
3497shell.run(\"clear\")",
3498 "x\001+)JMU04?`040031QHM?,?/??)Md\016?>_?\025????_??3Z?_^??*'?2??D//????|{%?/?V?I???\\??G??%\019\003 P??/-N5dPQ?z?8??r??R?\020\"?i,?^??????????\012[?Yn$?k??\028<?8?\024??\002\006?K\007????P??7?Es??[?\001\\?I?",
3499 "x\001??Aj?0\016\000{?+?\030\008+i?? ?<!?\007ki?D?Q???? ?a``r?u\029?\\?\026]\021?J?1\016?w\0013:?e?bä8\019i@??\020o6??\028`\023???^|?b9+?B?d??%?D1?\024??o?p[[???p?????U??9??\
3500????\001\029??!??\030?C?H?]?R??h????}3/?>H?",
3501 "x\001?X?n?F\016??\021[?EB%\"R?\007\023?\031?E?\024\009???a\024?\\?(.?\\ZR???{f/???^\004X\"w?s=sv?(?\017{yr??????&??\005[?Pl.\021[m???\015?\127\
3502??Z?H??,\018?*q\025m??D)?Pc??*\127\012no?`<????G??@F_E?K6c\127\006??~\"????N?J?h%?I\"v??<?RJ???oA???+???\028e2^?_?M??\006?v?b-u??R[?<????DY?uN?&0cnh\0237p?\020\025?\021??+??}??/U???)\029?*??e?*?\031\004O\006?[?\0281%?????a???B??R??J?R]di?\028P??.\002?\025???XL???\011???-?>j??\011?\018?N?m{lF?2}/??2?????\0318(?6???: \030D\014?\009t?2,?,{MK\031??y??\025Ac(F??X??d?cF?s?O1#????|\024??\009;?1?9?vG??y?Y}\021????Z?Z\012Z?g@~1??#E??\0250?Y)?K\019?Rl???????\012#\0144\022????>\007L\030???/?|?&??/,?h?\022G\000\\???\027?\127??>?\012\019\025??0#???c?'\005?D?l?Y???T\021>?U?\009W?\019??})?P&\029]?\015?\028e?N??\005??\007CB??eZ\016K\001?,\023\027?>?Q?9t?8K?%u?Q\029\026?J?J\001???\002r???8\019\\\
3503???\023Ã ???\0185\025!??:\021q?d?'?2??`k?\019???1m???\030??\002??p?HR\
3504=\016????\0084?D?w???mq?'??\019\008?\003 ?:}\016????V?\002?a????/??.?[Y??Qv?\009??9?\019???ks\017?\
3505?/\006N??\028?????0\020?P?A??A{?h\025?\016?NO?????e?V?B]????\023rs?)4?o???R?Ad?[$????r\018}T2p6U?????Zr\029???<?a????0'\031Kd??u\0244?????m?w\008?1??\014?$??;\003j??V\0035?????o??\018t9}i??\029\018S?C???o??:8????#?????c\001\0029&?Y?|??0?????>g?k?=\0265@\000#?Y?3?S?\00375\012m??\008E?u\027?m??!??C????\028j?f?\021???El?5\017`7B\015,?f\001\002??6?\024\020?0,?\0263\023??\000??#*j??[\019N]??+z????G\
3506????ÜRt?m?=?3\
3507??????????K\019?\025???????r?\011u?A#?\017sG?Q?zN??\025}?\027?S??E?J?4??jH?\006???h??K]7\014?\
3508??\0113x\018?????\018??\
3509??L\016??\028?S\011y7?zo\026?\019?1???i?????mK9b7aZ????0?\\?G?\016h#???hS??noOJ\026 ?6\029\127 z???-|???)~??\029??Dy?m??7\003\007pb\
3510???d?7?}\005 ??\007\029Cc?G?{?xr?=?1M*??L?H??{\000???<b?z2l\01473`'?|??\020o,?y?$???:c?2???\007?,Q8x\019???!C\019?G??\
3511?DC?\014\026??\029??\
3512)?????I???3Y??D??)`|?/?k??? ]?o?x;??{??m_jur?~?-????\027?6O??????Z?[?@?;?\017?D?=b??ym?Z?\001/?2\017A`1?????\001#\024^?A???\
3513\014/?\001???o%?????OK??9;+???$?x?>??@Kv$\127k??Wf?j{\014L\017r\003?j?Ol?R?\0032t3?\029z?\018\030\001O??\"?!MXhqK&yb\007r'?¯\001???>55\008i^\030LA??D?RW?9??o?\009o?",
3514 "x\001??M\
3515?0\016F]?\020?\023d?3I\011\"?\011]z?Ig?\021kJ???\011O?????}C?????v?\022Up?BO?N\004\019J??{-??YA\026,R??}13/?n?%??R???-?D\014H??g?\003????\011\026??G]?6?????\127?5\031?\019???P?\003?\016cO1\016?\022\029?Y?z??\031?9??????V?2??'?/\019\003He",
3516 "x\001+)JMU04?d040031QHI,I??)?a?`\022????m????[??\029L??-AS?X?\016????@0J???Z?\016\127E?\005L\014Pe?)?%?E\012?7\002???\031??,pM,]??R.:\012\000Av(?",
3517 "x\001?=\014?0\012F?9?\015?P?\005??=`l\008\019\017Ad`?zw????\
3518?\000????l??V&??(t???iCk????\023??\028s?s??;$???V??w???K??????-???\023T\022???\015]??9/VG?1>_??]?D???\029??9x?U8???s???r?\005\030\003? 2?\012&\003?2?\002??????L?\004??Y???o?X]?????P~\
3519?\021?\002??",
3520 "x\001+)JMU041e040031Q?K?,I,))?L*-I-f?????\001\002??\030\006L??C????&H*3???R\025?j??-??\023m?z??J\
3521??Jm\005&\006@?P?ZT?Z?Pppa?\0197???W??|;???O-?\024?-%\012S?^????.}wm??*?M?\007'\000\000y\003??",
3522 "x\001??A\014? \016\000=?=y11@(\005b???/\000??\024?????\024_?m2?db-%u?B\031z#\002E??Zp?|\008?\021?q?q B%???1\018?A?????#???HpY~???\\|??X?\021???\014Z\009\003'.9g????H?\011?\022?/?\029?\017\009?<??{o)l?V8~E??\017L)??>G?G?",
3523 "x\001K??OR03`??RP(?/?IQ?UP??/-N5R?\001?e???\023U\002?kA\" ?\017?Q\001d\024\026?p?\002\000??\018#",
3524 "x\001?Y[O?F\020??+F~?#\005??BaU*q?\
3525?nW??\005U\021?????<6IZ?\127?w?b;N?\001??t???9s??????????????8)???K?l?'?-?2g?<eR<\009?X?+\017?,e?8Q,\016?(IE?????G<I?l?'E!R?\007\001?\006o\001\009o?$??]v????qR0^?Y??r>?&???????K?f\005?|??q?w?'?/E??>???????\031UAo\001^\003\1276?1?\004?/??\021/8???=5???C???^??\031?{_f??????\025\024????\127&\025~)?I?d)?\024??\030?@\
3526???Q?\
3527G\029?+D>??(.?\\e9X??n????\015&?<+??2?\025\029l\000??0?kZ?~?\023i??\\?\"-C?4?\001?RQ_?x??9l+\026??-?L?M?Z?.;v??\"\012?_?\001?nP??M??8?E????j;vo??~}\127sG?\026?^??=(!\005b\007????<??K??K?.EJ?!?\022]??LE?R?k\002~??0?f?\018?\003?\012&D?K??\008;;c=??-??g???>???\031?3?c?5\\?yN?!?\
3528B??\003??Gn????\017??L?w?\026??'?E?$??Q\022???'i??a\030?A.8?aOvY\015????>????8??\008k\002\014???\026\
3529?q?\014\028\027?uH4B??\021?@?^d?5\009?$?2??\003\009?O?\004\"k\014?\020??\022yH?U?:G?\001lhU?Y?\"\027??)da??T?p%T?DQ36w?7I???c????\027gC?<b?dY????o3\030\127?lx~*??T8|?)\021Vc?\026\025?z&?*???\
3530???bT{?\023?\025Ds???\005\\\024PEu#\021\019[???????????d??????4E?6?IU\021pr?\"?HT?B?\0281\021??\004?\
3531\005F?B??p?\018??ZA??\006???\
3532jh?*?6??%?p?,:?wP?e\031\005??\024?Z'C?woi\"??*\031?P%?0??%?U??i??[%??6?g?zF?\006?mOU+?fy??\022X????tK3?????w?\022\022????9)??/?\028?l??t?)!???i&???!? 3n9\"J\
35331\011\015?:j,???2\
3534?\025&??\005Q?\018??\030?\023?\001?9?f?k9?6????\011????:????D??\028pF??+~Z3?\021???@ws'?FF???[??Q?-R?*\019*??0??|?\"k5??d\026E?51J4\020???+?aT{a?l&?f?\"/u???7#H???,\008\025g?0\030?K?)UC6IX\022?TQ?*?Q]&\127??\028\004\029}p 3E???d\029k??@~F\"??B\028\029?+\029???h*B\0264p(7\
3535\014?c\0021C\008???M9`\002?U???\\??|F??\012?\029??N\001u?-c?LZS???\022?\019??y?\030?m?g\023??0Rw??\022gG?Yg?\025\127???`?QL?'?9S\000Q?\\?\025??\023?\
35360\"m\
3537z??\001?\\#tQ?X0?>?\029??B?N]$yv\
35386?B??\
35393??5\029??\012O?\002?3\011?^Z??Qo????3?-\024?I???\011,s???<2&^???\030!\009f3???m?V??\\?T\009ROc?>???uK??s?}?b?Uc?\0178j6?.(????S??\012\023\023\008?p??\030[A?Tt?\017%TV??^k`??7\022X~?@?\030*?V*X\021xk&3q?uL&K?]p??4?n??WX?\025????U\023???aE\014?\000?RC?i\027\027?q?\024?\031q\023?+p??\004\
3540'Wm?u??E\021\001\026???\002???E?:????_?n6d\012?\006??`Q\016?\008?3+\004M_?\020X&.;??U_C?=?@\004\012??\002m0??\004???N\024 ?:???:e?\004m?Cm?J??\024?\021wx?\\K?d*\007tr?=f?v1??e????\"U?brr?8?ww??V/?_5??S??j????\020???n\030???G\030???i\024??MT\031??\0040=Sm??Z1?n.\027?#.}?\031iV?l????;K5F\0267??M?\\??\028f_T?L??*?(-.kM?p???Mq??F?P_?Y?li|h??????1??U\023\005#????s?Jr\015A,?????H`'G?\006u?e?\009???{??VE?zP??iL??p??§%\024???\002~1\026?",
3541 "x\001?\127{\0277?.z???E/}??^??%'??\027e?#?\019??????d=?y(?)?\"?l?\0183'????V???nt?))??????n?P(\020\
3542U?B?l?8K\014???????~???{I?7o?????jQ???6y3]?\022????t~???{?\127???K????\023i??Vi:O??\015??b4?%W???8Y????y?~?\015?\007(??t}????*?/?\009~'K?]\
3543/?i??6?U:_???p?FC?b?l2?????\003=??N?\027@?\012gYZ\005:L??2\025/???%K\002?p??~4???(???????\020?\023?\025\000??$?\015?f?8?\002????d?Y??Ez\009L\0318??R\021?j?????\026????/Q??\001ykAw?f?t??8v??4[???r1NI???a????!?\029TNW9?\016?|z?\026??P?J??\012G??r?f:?\023??85?|????\014???U??\\??I?[?\000?)?\014??\018???vp_???A?<?\0127?5?t?T\007\\?\002<;Lf?l?,&B???.??d?QV?Hg?d2?\025?e??:\005\0029?f???????\127???\027?\027??\
3544y?}Y??y?N??1????|z?\028SJ\008?K?\019\012z?\\\012??\019t?^\015????u?\000u>???l]L???m2??L???|\
3545zqN?{L?s\014????\0083!}????\001?o\022??i2?(E0?fW?m\006\\>b\020??R?\000'm#9tXI?\025??[?#?|??J.?s????\\P8\004?1??=???\016??b3_\003?#??b??????\007H??b??4>???4[&?\023?I\006\026??\031??\020?\021P=\011???\017??sN ??s???\020?4?%??rxi;\014?q??5?w+Kg`q?Y:A??8YM?/??\0033{??z+?\003?\029Z????:?q<?\003?K?>:\000?\022????D?[\001?]2??f\014bv?????g8\
3546g?!?\028=A;?\
3547@??\005?\127}?9\002Y???s?&\014??\017<?J? ?N\002L?f????\011\027\020?1H??<\009H6?dNH\021?\005?7?6?I??\003\011a]???z??t????_?????\000??|?E??h1????C??R\009d??\012\"?:?\\.?\018 ?\012L l????h?E?0????-C??%??`\026\158\024??????u?oN6?o{?\008??w?e??K??\015???63H10`?\031\027\008?1&#\004?\020k?\003g\031^?{@??S??\001?z??%?Z\005?&R0a???D??LRdn?A?\004?d&g;????'?;3??A???\018?$z??^??p7?S?8\007?\009I\027?\
3548?w?L?>?\008??W?\020??G?%???@?V9?~?\018\014I{?V?;???o{???M?\025?Lp]\
3549????????????\007\000?4??s??v?d&???\004\004M?h??a=?,;?Y??Mt?s?PdZ\008????\0124Z?A0} F\014???\019?\003?1???X??I?F?|??????3??j?]?B???\011???:\005;\000?X?;^O\018eQx??wNt??|?55Ok\001???\028:?v}??6?Z???\011?\
3550\030???&?lU ?\030?'??nZ&\029??lNl?wd?P?\016I?i-s?^\127????y???Ez=\029?#\012\012??\028?~\006i?\001??e2?\\??\005e<?a??O?6??\002?u?~\127??{?3????????L~\031???~\"????S??v\018V?L??td?'!?-??z?H?????\028z?4?\127\002?l?;\027\006???s?3\025??\006?\019????o;?z??\020>???\019~?G?\022~?'??W?\022|?7?\015??\031?\027??zI2??o\004!m??????7?o\009?*.?\023?\025???~+}0??\021???K?M?\007T??j?\002??v?q?Q?y???K??\000???v???\031??V?\015^\001??<.??_\023_?\026?M??&??q?\007??cI????\018??\004[@?q?mA?X???G?iF\031??\025-:?\018\023K??^?\027?W3????<?XLGX\003@??;(??^???c?2??a\022???e??7F??M???KP?m?h?fIw:\031?60?\009W?\1272\014\031?_\011@.\017\1273[??tD?a*u???K%\127Of?\005?W?rA\020!+f?p?\031*???9U?\001?`??\008X5=Lt??_???70?\002?.??o\001?\025?o?U\012Q6.\004K:\0165\004m(?V?:????@-I???[\1274?2\031?V??i\006q)?\025-?[vb9?V?4?V????cb6Q???z?\005?\024\0125??\008{Y$\014?~?x?eN?H8??x?g?M?#??a\018v???s?\
3551}H??c?~??\021\
3552?eM?????\\\002ZNF?\014?A1l\
3553?t???r?'?6\030?5w??\001??\127?A'9??\
3554?\003?\031\020 ?F0??O?i\004.]???BS\027.??\000[?????Y??=H^?C?\001?\030????)J|'0?u\028??9?W??lA?P?!???\127?\012?\008?8??\031Dh\015\017??\008G??_????\
3555F+F*#??\019?K?R??\028_???A?W??N??.??HyR?A5?vA?F\012?8???\005?????9?$IT\127?Z?&???3?\015??\005k?^\
3556g\\???\012?;?=?2?\028f2rb(?????C?7?\026\017?\014??\020??~?q???>}??\011?*?1\029?\\6??f?U?B?\002?u???sn\026?#\025N`?)\007??????\134???G?\014W/??\020??Q`u?\012V\026?c??h?\020??X\\e:?Uf??+Z??+?t(??!?X???)\022D?g5\020q\003Y\002W\018??4?U???_?>\016\016?Hg0????\017K?a9$\025?W??h\005??\021s??J??A\\Vi?V?D?\004l?D?????)|\028?k??\017\019\026u`o1W?{?Vr?x\019???8?\009T?w?M??g?NOj^Z;?]*??1?zLBf?P?b\004\001?\
3557\
3558?8W\026]?tD?+u`J\0258A???Q???\018eR]DPE?.??????3\028\012~w,\009\017\
3559?\027W1?`\001?\0182;?[q9k??\004???\012j???.?g\"\017????g \"?j??t?\011?}?(??\012?|?f\009]?\007?)??_?-a?\003??7??D???l=?l\001+?E\131?\0068?\026?\023?\019???p??sJ?a\006???{T\018N\028\009:&\\??????????@?\031\027??],VT?\020??;n!\0161???`\011*?\"S??7?\026*.?z?zA?m?d???h????\029oqqtK?0?}\023\021':???\023<?~?\015?\011?)??rd@?4b?~???|??\002v)n\014Q?u'\005\026<\022???\\\011N?_7[\002??hKY?Bg?B?V\028?k?N?)q\0085??B\012L?????`K?lK!?+?????L??6h????x\029txT?\
3560?%{?\\??\023E?*\024\015?Àg\018k?L?\007?g??HX\020\002?A[SY(??\006?x??C\026??\"0m\031?X?/E&P???\025p??9\001z??M??z>?.?\022P\008?4??W8^??[?M??:?-p\001????7??e??'?(?J??9????\016?\028Q????P\000?u:\028?????C\151??6'f?????\020|FI???\007UCm\028?\007??]??[\015p?~\
3561?\024?(?\011?e????)Q\004?!?\005=?o?O??[:??\"?!??????]?\023-?\000???\
3562?????w?h?5O<2?Z(-?\127X????\003??qU9`K?wv?\021?H?\0241?@0? ?Mo?r??Y?????dI?j?s??^Nh<??(H*?u?<\029??P?D???Gm?Y/\024x??W.o/??\016S??;:???\012??\030?w??P?%\020?`??D?z?X??-3?\\??D?\008?t\012?T?????$????\018?(?;??\
3563?7?\004?Ur\009??k44\005?c?>r?C?+?6\"E?~\027?ÉSV\023??N@\016?\012^S$5rl=?0k*+_?&T?~??o??k?]\"4?y?e?Ou\
3564???\021D??dI???\004\003?\022??p?\018st??\024.?n\005\006\003r5??.?h?:b<?\026kp^\030?\022e\014l???\003L???J5Z`?u:?\006??;??J\002??`?\020??o?P?sQ?\
3565S??fb??\028\017?Ch?????W\002?\001?Y?)?K????%_?\030?7)?I??~?s?o???\027???O\127??O?L\014\015?]T?\017\012\009&\021q?K?\016\0147?O\016??`\007\008\019??\003G???\006???.???@\024\000\030?NQm???{?\019&*?G????\017?\003??W???w??0???s????\024?\0182?l&DQ\008\027?5???L\016???~\"?\000n????O?M?\019\012\026E?(<???B\027\001\016\002?g??=?\005?E?????\021??\000???\011\024?\027~\030\028??l\017???Q02?~?\029??\127???&\008\011U?!?\006?Y\004?\014G*?gt\004???r?.\030???\005D\012|!\\\022TC? ?\005{@? ?(?\022?Le\031k\025??AR??\
3566??E?bs\017??0ch?\019r??^\"?\017?}X?\018?x\014?#nj?%??$?\016F1?I)\000??)T?*.\011?B?@\009?1\025?e?5Xw????\004U\019???\008???\004E\019???B??Q????+??R????\006??B\
3567??\003??OS??A?p?;Rc+?%?o??.\026??\024??Q???v[;\023?2?.v[]P\027Y\020T??e???*??c\022'?l??4?*\025_hw?\017???m\030\025\009?J6e'?c\018?\011\012(?o??FU0?\023?\014&X>z??tV???+??\029?\023?0?c?s?\002?\024h?4???\029?x?\006?\127\023GH\007c\001cL?Xd\
3568??A?5??????\015????\002?i?*??\018i???%?@a?Z`??>\012??\012)'?9??l|\007xm?????EZ\017? ?n?M??\004\0179\\3?H%?|C??CV\1276?(?????\\?-S?,,W????-&?????\003,*?EuM?\022?\025?oqs X?^?m?\020V7\018\006\022;??w???\018??.?-??!\024{\008jPo??\026?&??-?79&????/a?\018\\?q\008?\015a#?|)??\021?=?A?1cG???\016??E?#??gC5\006h??^A?(\002?\006b!?r?,??.?\005?d????¯?9??\026??\012f?XV?U??6\028?%\016?#??\\?y\014?i?\004\027|?+?H?#\018?a???)???8W?$?lq. \016??b9.;U??e<???U???\003???9???q?$?D??!?w?q???k\020?\029\004???\019?Y?Z?\025??+b??0*?????\
3569)*A?????'???\006?\031U???Q???\002%\011\020'?2?\004?.\019\
3570?`\026UX\017h?C?C5t>\"???d\\]?\016^????\012????w\021bP\005i\030)\
3571?=?o?6?3\030??~?\008???`???\031x4??M1???)?>?\0310?\008??<????????\018? ???\
3572\017=\009\"??Tf?e?F???pu?`?B?%;???^\0127g???M???\015\024Qx|f_?<??nB???\031?YHm\007?5\031g\029??&C\004-?o2]e.<j? ?0S?J?P?5?\007??$\
3573??\0251??/pA??<g??\030\
3574??\015???\027v\014??\127w\026??g???\014N??bR?!?C?wz\006?G]aD?\018???\024a????\022V??u??f?N??*??\006?z8?\031\003c@\022q\0114\014?L??; ?N\008?N?\016?%? E1?.`U\028\024~?\
3575&???-p\028??6\003\026?]??\018,?\027(\":?s\030k\021?\014\"v??.??w?|\011?N????\022?z???\022(??C\024?\022y????$?p?q?;???\127?@??A?f?A??i???O?\004b[?\023\021?\008\008?\00820?\002?5t?/?%??D?w\018?c?u\005!??{???|,\002??\007Lh60?\017+?3K???6>??\008?>O??\011!??$\015q??q??m=?7???\009Ng\127?^?a??2\015<tlE??p?`?/D\015K???\\??*??J??\"?\019?m?o?@5?c?\006?\024?\020??|?_\028\021???_$???i?????????}????'\031?=???G]?\\??:*h\021?\027J????=???XwT??s??\011[@s8\008?-\0238#@???\030h?'\007??bp???n?\009=???t??Z\029?\019aV\0124??-??\031^i??Wy9?\
3576B\000\025\0008??yJ\
3577;$??pú\\\\u?z?*\007UC~?`cu\005 J\019??\018??4??E?????O?.?3h3?km.{??h?=\\?;?\007?z\017?1??\
3578?1?>\028?Pn\006???5d?`??\
3579+c?|B??'?I?l?\000??S\02345?&<?P?q?Xw?p?QS?K???\001??\022??I??S9?A\017\
3580\030??z?\017:\029\023|.??t9??t???7???Tq?9?\021X?F?\\???l\011R??U\000/??A?k?a6~=????]\022\127p?@??\
3581??.??qr?_\006\004????\014???????8F\009Z?*S\000X???#????J?\007?1?\026\008-:?Wt?i3??\011l???`\009??O\"*?+(???????\127???o8???V?????\015?\031????x^?qab\127?\003B??\016???82???'\007\017\011\01498? ?6;\
35820?wp??C\008\014X??c?\"0Y1???%CED2????B?O?\001????\001??Xxl\017???_9?? k?\001???E??\021?\0215+ZA?s??Y&??Olf}???8?\009?j?|v?\\'??U????6???\016?\028;o??\011\014(??9\007\028{s?Z??\025?\028t?\029Ln???w??\012?x%\026??\025?\019??_?P`?r??\001?\001\002\012??\001\001??????MFGt'?H??\012\004\006\023_%??{h?\002??SM;??>\015?\008+???)JNf?\018??????u?\018?\015?\031\007|??(??;??{?@???5???\031?G0?????????jx???&U???:4UX?+??:?aN^??LH#!'?A\017\021\014z\030Nz??\029~???G?j???V?GB}-r\022?J??\028!\030G1t?-??\028zD\000?8?I?3??1?`?|??,??\127\011\022Y\015I}\003,??M?\026(b[[J??\\?L2?\011?\031?\006hC8?\003??\0033?6?C?a\022?\"?\
3583\000f?\027?+?x(? QX????@\007?`?*lwH?\
3584?*????WT?|\
3585?a&k(I??d/?.\012F1?l?\005?\020lb\009+\019S??B?P=H/LF=?\005?bFl)?V?b?????\000?\026?d ?;0?:X\003?\030\018?\027??/?f??/??@?q???)?l6?l\015??3\006?B/?>3???;d???\015q\009/??I@_{\011!\004<VWS?$\003\023Ci??e?F6n?cX?/?????2?H?J???G\0205?EO\031?\029?\028V?'{??\014?_*\030??G#??!???\015?8??]?\022%\025???\014\022a?bO?????/?J??????X\
3586??^?b??\\??[?L??^??~??p\025(?K&?\020iT\127\127?}?b????t?\012??7 ?w?CE5??b??2nV\000\031??*C????\019wD??\022M???\021H?7m\156\027A/???\030\015i?\
3587?\023?M)??\011?c|\019V`?^?!jS???]????Pr?\012P?\031\012\006?|???`\023?\020HCn?0@\015??\005?5?\006R=d?R?\
3588W[\015?Ì??\018????????I\026????\0036?8cC0??O??r\
3589\026?\127?C?\028<`??\031??xP????0\017|#w??t\023\\?`????`?`\001?L\029??o?[???g????\
3590?kX??;\002Z&?jU?Plc0?\006R???$e%9???\127\016\011\021??q??*?A??o??????g??\024??M{?n?\
3591'?*?\014d?\"??@\006??5YV?-?-/a???\
3592-\021??\012\000???\024??~\021?B+??O\025B??W\015?¿?$VW6???\002???XiC?36?g4?s?{\"%?^?\127?v?leh?a5?J?y?t??????@?6?U\026??\012\030???f?s\030?\030$\015\019?2?5????n\018??l\020?I<j?\005???\
3593???-????????W?^?=?IJ2$\127??G?f?V$?(??????p?]^\028?\024?\022??.\008K@???D??b=e??t?O5??\0209??vY??h??8Yd\003?mUh?\007????j,n???\031??(??\001?\009??8?`?d3??4\011^:CH>B?5\008B\029?p?????\009?\001z!\023???\0193?\026\011!?)pK(l?e?\025?b\134Y?;?\004????Zs?\004?M????\028s\025?]\003?;p?\
3594?$???\003.v?D?\030?,&?\015\030?b??#??\016?j&?|?\012???U.\009\030??N?a??:=?c\030\003\007)!N???'\027????\004?q4s???%???\012|?8?X|P???gXB\022Z?[4?\022\014X\008V?\031????*C?3?Nn7C\014\009?t8K!q?s'?\008\028l[ ??1??c?E\012\030&_??d^\031?\011????by?'KG?|???)?=??C6?}\029x????1\
3595g?.+?;??8?\012\\h?{ \
3596z:?!\016??#l?\008?\0258?\024?GOXd?(TE???l?\021?O??\014?o? {\017?\017?w8rPZ???q???????\012V`[\027?IT18e?.`?{?J?3?_{?c?I>?ew??????[?\018\127??????~??)?\017?/[Z?W5\
3597????,\009\002??MB?????'o??1?????8??g?n?]?????<4\001C?h?4R\011\025\009\030??w\003Y23?tS?a1A?????\012?^??Y??`\000??\011?~?]?\011\014??k????x??t?-?????\008\001{???k.?\025??\024?D]P?\016???\026NN?Zo????q??:????V\022??6???/\007?T=?????+XB???c?\016\\??\127??ju?r\002t?x?\
3598ub????#?\002\021\016\001?{P\015????\008\
3599UI\017N\
3600k??\015\006?jt???X????\021?;?],??ra??????\008?W\011d??^??\\??\031??,?=?\
3601???q\019\011s\026?In?8,?M`?}Y?????\
3602e?n???B??4X??p?]??h??\023o\017???\017$?6d??_?`pN\021C8?\006???3\005%CY???6??q2]\012x?@?#?\015q?9\002?V?^?\018w?f?S?LW\023\019?1?|??rU?\\\023-?\019???n\127\"!\001????p=F???%?J?XE?js\022?\\???\029_C7\
3603\\\019????jD??)\001???\
3604?)d`k?J\030???J\"+??6dpF?E??6\012Xq\006?D????\001??\127?\025pz?\025P?\030?8\004#?\018\001#E??J???/|7\019*\031\007C??|j??B?\022Sr\023\008??n(~T??????1$?\019?\012??\005\001???U?l\019???\021\029\008??I\020???\030\"?\015~?B?q?b/\025\030\022\021j9\026?\008\011?\001MG???1:\\?\021??c?\027>9L?_?^\"\020^?\030\
36052???F\026?\019?K?G??h,??B???d4}?c??\014,???`>???<)?\025i??????i???\029?9`?\000$i?\014?(\
3606???u?J)?????)\004O?Q69?\030\
3607+V~??p? ??????/??z\004???\
3608\024\005????d\
3609}??Ik????l\011?E?m\024Y*K?????A\002\0264?v??T???~?z??]??^\029T\014@\007Ã¥?\008???\021:5s?\021w?r??\027aa??!?\009?\030??o?\011X??\012?5??M2?J??\006??S7??????\015\003]~\022?]?r?JMVY?u?E??GX\007???o\0038\006??5???l?GKGvm;M????4y@\017v?/??zVR7?<?a\
3610?B.????H\005g?@\018??\014??\009\001o?\031mX?q,\012p???^oUI?w?R????\012?P??n\012I??4?L???\004??NCL\008?1?\022\027??g??????5BgMYx8_?)?<(?Hokn??\006?y\025?x{??\
3611L?]?\026V?zj?A?\027\007!4B\021?-?=??zV???\016???\006q?]y\0238\017???\021??\025?s?z\016W?P?\007?P?+?|?e??_??????\
3612\028o?]z?cr??a\022\004%?\022??Q?}T??\0192!?\027??.<0?\023?>H?\028\008?|S?S2\023]8p?6*Dl??Z\023?\021i?`???????\028??4?x??hV?a???\009%EC?j{\029d????`\
3613D???M?????<m??_??Q?l????}T???~?Dg??\021?s?\005?9?q??)LG'p?>?>y???\027?O\003?\016?\011%??g\014+m?pZ?;{Vh??\021H?*M??\001m`??6???)6?\011\027?A?I\026??\\?Q\017?\029??i?z/;\016?J+\015? ??R??Y??'p?:???n\026i?_`PB?m1\006-???V??5?w^\025??\127????|r9&???*?\005??\025??xyf?9?X:??Y?\\?IPb?\009?~E?f?Y?ce???\"k'???M?6?vL?&\016 ?\020g|,?T\004L\000??T?\
3614?\007!?\021?O?Tp?8-&1??\007?\020S}?\027??\018\016?P!?r\127??#{?<???\0166viG?0????\001?R\028oY-\022?v?J?2QJ%\\*?C??%?b?y???c\030?S??Rd?\029Q??H?c?_?`\030\027?A?xD?f?\002?^3?\007?7?xz???\020L/?{I\026?2?\025??yp?\011=Ol??\008???^>?#?q???h\028\024????k?$R?7V??W??\018\024x ?\002??=:z??????\002_\031?W??}\014\030b-?\020`?#?2?86\002BL\031r ????Z?cl?`\004??|wu?X%@\011???#&?\127\005???\
3615C.H??????0\020\003??^+P\011???`ddMI?!\026\029?*Z?2P?C?#??\016q?E??Y??v????bk?5?\029\020??\020?\028f??\001?_\025?|?X\000\011oy\031?%\009??u\008h??f?'?/???\015???6w???=W?\001/p\009|?o_?\001?\001?!?Pa?\008\
3616r?:???\009\028??I`05\006?\009?l>?\\?'?1?D?(\023\029??????????\029?\031J\031?\007I???\027??\030?Kh?\028\\?puC?\007k???&?X\031}jl??>???=>?D?????[???\030?????\031\006*?^R\026K???H7\015=?8??ù?l?\004\003]\027???@?(?? \025;?`\127b6\023\000?m;A??;?&l??J?l0?\023???\030p??g?7?E???5_;\
3617??|?\008?!?Rh\020??&?\017?rp@?\026??)??\015Rr]h??M?&?\
3618\\?\005m???\030W\028\019s?\004?!?-?<???i\\cc\016!?5gE??+?./8*2LK\127\009??CWU?K\017\020?<?-?z?'`?RT?0?\030\
3619? B6?\023L??!y\008o?????\020??$_$?\019???\014r?\011???V???\023?\027Q???)D+#?C?c?J?(\007?yM\024H?T\015??\020M?bP]?h???)??N*??\009????U?N!??g~?\024? ??\028\012??-J?H\022?J\
3620??\026f?R?!?\0033[?\030?{??\0039<?\017??9R\009i?q???d7\017?\017??:&???h\028T??>?????\
3621~_?--????P\015??D?q?|??\
3622q???\
3623???v88\001?\001?_?2_\"\000\025?)2?i??SYq?`?P\030\
3624?D?%\006??Bd??-G\014???\016~??\008?\007''?\028?<l?\003?'?\007\"?\015???P?L?\007??\016??5????}U\003Q????.\009T\"$e%l?S?SB??????R?\022\004<??RPh+?).?????\
3625(?\024\015nX R?&\031?Æ??3?Ms\007\011@%==ajr?\000????`q\029-N?8t??\025?\014?yH\004?C?\027R??Q?\002E.?gB?\000??\021\006?J??\0168?\\\
3626?:K}\031T?y?8?lU\020z?H??%\004??%?{-\012S-N?6???\017S_???y????\016?\016\008?l??N?t??\024v\011?=?J??\"?V\015q?\014?2???;?S?F?{?h?\002??52&3!?~?heR8P`?\027~?d\023?|7?z\0067]?\021?3?\004?)\005????]j????\0233\
3627@d????=<\018?z??.?\018W??A\0065\005?\006&???|\004IR??u?-?h??B\006?L\027oW8?T?$u|?Z\026\011?~??8|Q??E\012`??\
3628\026?\009?\014??yn[$5BPgv?fhj:?fl\\?0?\001\008V{??[vS?\026N???\003 \029???\030??\030?Pv?\029??2\001????????\020??&\"4!??]??VC,?\
3629??\000\004B%???\025?ZT???????f?!???\017\0262^\028????\017?y?B\024?1?B??8'?¨???O???k???????=d?5?Lf\031??k ?6H-?Z?(>v???o???9\014????ZP\\$C\023????!?\017&\
3630? N=\"G??~#\
3631?x?\008??\\?????t*?yHDrb\018??\"??r????\017M??\\\006\030)??CJ7??\022???????\031???.?%\028?\009GByY???\024?,k\
3632\0269Pa?\021?\
36331?1!??/?\003~}P?2O\001d??)\028b\127????\007x?3?\026??\019??x?iS*?81?:_??C?#???ZX???.\019\\e?.c??;?f???]?]?R???_???V?9%\002WI\029\006\016????@?`\014[??Y?me???4\012?:??T5Ó?Y?L?\023??Y??J?\
3634M??\004C?a\018~+??/?yk?0f?Z?h??\027??P??v?7??jA?eD|??}w?T\014\004??6?{?j?MWPt\023k??\011?N\"????Q9b?1sT???F<??\018y?ct\026\025,FV\009*\030h9/a?r?\014?\016?2?,NL\000K?2?eH?%??/!n??f?=?o4?\127-C???\020\009?\\??!??\018???\
3635)Wz???G\004\031?\030?2?7\015\017???4!\016?\014V@Y?? ???\022?\011F?`=?h%?\016? r\026\021??=O?Ø??j??l??\127.??D??b\015?\031?QW???1f`Qt?H??l??8?fc?\015??????L??u??~v???3?N?\011??]?\003??D??\014??7?*?P?N?\017r\006??\004???E??\026???A1?DV?J?A\004??j?Y?\003?5?\024,2?8i?<?=?-???m????Vw=\003??'?w?\015d?????n???\012??,4dA?c9V??w?,?#?^?`!?c?\026@{?3??\020n?\006???Vg??f??\012:???io??\025?[??\027??_?\127?,r#?g(\003\\??\021$?h?\"R????n.R??\004b?\026p?V?O?FtC.y\"????#???\022?\030?:DBy?\012H????\015???cX?u??w\026+?0?eT?/??W_??/?]6??aq\127?\009\003\027e?ay\024?????O7\018\001??\025?????%??????\017???,6???s\003????y??????c@j????Nn?C\008?_??k=??Q X/?*?\029???=\003v??yS5?\026????\001\011????lp?y?I\016?4\012??>?)?T?\021?\127\009\022tkQV????A????pq?F??\003#?n??Bgoe|???hb?2??Q??`??waT\000?\029\025\019&?\127\"#??\007??\029\025\015 ?Ok4?R??\005????FB\005??0\014\028?ZM?p?jk??m??*?wb\004???F???p?J\127??m?}??Z??\005??\029\"s\027?~\015AlM??\029h?\005q??W??m???Rd\031J?)?T&?-|\023???\"1?.\018???????? d?6???^??)tv~\"?)??q??\002??}?z\019?\006:?*Y?^????\018???$????}/0\026????Zn?\004}/??q?*\0201?8qST\026?\000???,!\007\024??=\027%B-\029?7???0?t?/wE?\016?OI??Ui??Y??6`U7\002Hi\031???Y\
3636???)?x?R?\031?C?TM??b?\018??O?[?y/v?\018?h?@)>$???L?M\017?#\015?b9w??E x`???????\014?H\014)?h???\027?G?b??!\\???@?-?i??}\031g??\
3637]?4t]l?:hHn\025??u?b??Z??Zg???y??Ç??\004/|u?Wq?\026???X6???\015???p?Mg\003R?{\025?=;??W\025????rg+\007L??eP???]?\023??<???Sz\029?,TC?t???X\027?K+\
3638;?\003?ZK\127\019'??)\003m?V?Ce`\
3639?\030i2X???Y}\030\001?r??\127\0305M>%Qw?9\027??rjy!?\029!q???\003\025B?4B??>e?E\001?.\
3640???9?(?X?5????\029?\127t???\026I\025??hnyZ?q\027=N\017\017???H0??\028]\012qC\000\
3641??\029????G4?\031?y???#???wp\021\006X?i!?\019\003????\011??@\004\024n??(_?:\027\007\021?*<???ct1??D?gv? ??(JwC\011?\004 ?\011?\027N?????]\024u?`??\001?/i?Lp?x!?\025\017~?\021\002?T??\022?\009???T?\016?\026??_q??H??9N.??R\003??m?\016?8???\001??_+???o\028l????\004??1??\007????@?\007?#?9;c?A??\
36421.?Q\001'????}\023?,?G????$5BFR?~??z?}?0B?*?[??c?\015q]?\012?4?\006g\
3643?M\031::\
3644?&?\008/\017+l-??\008??T???}?\008\009????q??688\014\030???08}\023\018????)?? ?h\023?*??K?&T???rR?????\
3645\005wW\027??_?y??c?\029(W4???\001a\028?u?KZ???l\"\031????\023??\011w???f\
3646b?F?\016???b?\007?\024??\014??\000D\011K1Q`?<H?=???????\001?\0037?!?[?a-PC??X?\004\020p??b:?Q?s\017?2?zs=????p\"?O?\127?.6??]?\0110\018???~M?@L\
3647?\008CWdB?\019?t\024r\024y{l\019?\007a@\029????\145=?I\022\
3648????\019?\000?\012?h?Z?????q?\023<Fn?C??\0173{?C5Â¥???\006???N\008Xy{Z?\008?D X9{Jy\019\014A\004DT?6?\021???s\005?E????C\"??tv\020[?7?\024??b\
3649\005wP\012?S???????|#,?.@\011?}_?\006:?\023?g\0239????\008X\
3650?3??\027?t??\018??t>\020\023???????t?[??d????j?|?????\011s?^?im?H?@\014?3??J/\"?P??\023D?\000?L???S@?\029\
3651\023\012x?b?p???<??d?\
3652wG???,aj&\014?g?o??\026?k\"????Ub\012I????\003?\017??X?\005#??«G?'?eI?,DA??P\000\024-r???C?\019$V?9?@??@\019?t^??\029W\028?\008\002V?\016\000\005+\014M?1\020j!P?V?V\026+?U\150??)^?8a?\011\029.\023?E?8???y?J?Sh?$??_?+G?\026?o???\012.?A?6\\A???<??\028!L*6\029a???5?? ?3\020\011\019Q??H???J?n?:VZe,???v\007\007E?\006?wr3b`h)?g?L?,\002F\014}\\Pz\026n???1??0??S??y|{iOp\
3653\021?\002bQE?*\024&9\023!?T??X9{Z?\008W?? l=#[\005????3r?}?\024#?BW???E???d??i?8\030??a??}\014?G?\003\
3654?x?Q8???>^n????uz????\016??9?\008D??a-VCJ#]???????\002?\008?b?t??\005!??a?\012p(??=??YG?q?4??\006\158V|??????oq?\028\007?q(]\008?HpU!??Y+??????P\0091??gZ0:?L??wE~+?l?z??\008?[?\\8\000eV???????($?my\024#?1???8S??C??I!i??gD??/??;j?\005?m?!\022c?i??O0L?]3? ?_?j\\d?\024@{6??\014B\027?\029\004\015?:?????1???W%?1y]??\
3655\008LD?\002.??p??2\"B??ZI?Wj???VB\014R???????\007?K$????Z?\":?4sV??\004Z?)3\006\000\021?T?c?\021?\009?/\028=\012\021?F????D??\019???[?s\007I?\026??Uj?T3\
3656?=#lY\024\018~?2????_(???]???j?<\019?\003?$\020;?7?o; Q?f-??\025\015?cgD?]?\007??_\026L??h?????\028???\020Op\008\031? I??;??|?????5Y??^xT/??3?@\014\026?p??i9?!?????S?vU?L-?~?^\018\014P?~v\030\004??\022\029?~\
3657e`x?v\029??U?X?\
3658_0??s?!\022???ND>\023?6)\024?G?%??1\030?\005??Xq{?+?\127????\030%~?3??Z??\001c\003??r??\019??\026\007?5?\
3659]????b1\006\031?4??\005?????Hgi?v?'??\024?r?MwNr[???6?%??uX???f???R?\007????\023?\008???\030\0016???\006???\159??;a?f??#?\000F7?\005%?*B??\001?L\021`?\011?????A?\017D??W?&?\"R2?_\014??X?????U??E?0??`??\
3660??\005]?z?\"1?]?\024/?\011??a?cY)???\031&?\019!t\023)??;L???n9?=?\003&v>??\002:\031????\030?-Su??DTn?g?~\143_8\006?(A\
3661?? ?D\008?\006^?9/?\0047?*??\\?'???$*R_2\025??\001l\009J???\030y?\023?\031???xH?\031\000?C%N\005????|?[0???\0178\004s????$e0?y\025\025X????\015$?????\000????\
3662\017/?6?\005?\021??=\021m?\002??L?\009?E\030Rk\018?9f\008?\028??\023d\021.??o+?LS??G0h???>??z?\\?`?I.?:?H5?\003Y\004?z????8X0e?Q\029\011?P?X`?a\028?,??t@|&\016???\023\004D\006\127??\\?]?\002?G?\004>}6+?bD?U?/??F2??\004?W&}??O??K4?w=x??V???.????GUP\"Q\016U@y?V?3?A8QT?;L??;\".?????y?M89z???\127?<r?\001\020a\031??????\0203[\028?\011^1,h?(e?\024H\000_2??\028\012???HK:H?\026fS?<T???????Jc\\?\026\019??A???!?\015?\030$????K?\015DB?\014??{???\028??%??l3?V??(+\016\020\017??N\007*Ls?k??H?k????\003??\027\005??Iu????Q?E?{???\"?????Xu4(?????\018\0307\"??\022>?}?P?*?t?@??D?\026H8???\011%*,*\012??=Â¥_\005OA(?\127\
3663???_??H?BI?|??T??$?d?\\p?\001*?$??'a:\015.Fov???c???JB{?3?????\007\0000\015??O[?W4?Q?C??@\021???k\0249?nw?j?s\016?E??j1??.\025?\031?\025Cl????\
3664?~???5g?H?g?T?I??\127?.\001??3/???6??????V?p?^?#?8}o????s???f\011??PUECG)\"?Q\027#????l?3??M2??\014?b??(@?g\001T????\0081\025???_x&????a???S?0???\014]?.?B\018?????bx?????????6\000???\012$??????TGC??M?\"?@?[2??%??l??????8>?L??\023?P\009?\021P?C?>S???i???O!\003r\001e???\023??C?F1??h?T???.?\
3665?R#???m\004???%\028\011??\012N:????`???1\022^?lFz?@\005?\
3666??????M?bl??d??\003V?y??1???\026!*\0207???)x?/?????m??????\003\006?u??nj?P?/\
3667]\008???U?M{??E?\026??]{?????\023??.\\Ã’?r?.x\000?{b?\
3668\\(\029?+DWB?_??????F?{?>#?P\127???\030\031GFN?S\012\030???}???+l?j%?4?Y(????\0225YA~????\002H\025sR?\029lc??O\018l\023?Cu5??;?\"3??QN:k??*\016E}?\\-??D4?B-U'B?????@}\027E?G\
3669???@?IS???\003??J<?\030FT\016?????\029?\"\011P?\007Cn',(\021??r?y?Q?\003??\024?V\014?6\012?5dO?\021A7?uV1?!??n>?\017?Ve?xl7|N???_??? g???:5\
3670={\026????V?\006&B?}?N??\009cs?\021{?g?\"?\006???\0301wW\\.\018l?*H\003k??c<\018yW\025[?\031(?\017\009?\015?\
3671??7S?+?\
367290???????\008$\011?H?U?e??\019?\0055???\024?y?]\030?V?lM??\002??5r?}<??>Z\005{F\006?>?K??Y????\022c?\030???D?\
36737,?x?\023E\011?`R52\008Y?WU??K\022\"??>e??a?N?q?Jv5???E?|\001???\029??\
3674\017?6?\030N\011?&:?\030,??B?\026??if??\005q??w??mi??[ 5\0230??\020;j???\012?\025?r?Gs*Kk?+?7??\\?N????9?\021??\127??\023?????s????????U?l5??}??\002??p?}?L6s???9sq?^???[BR\024?Y?\027??1?Q??\014T?1?????L?=???P<4????a?º?>??Y??v?\\4h?cpNcu??g??j+???U_4??2q?\018\022#<\019?\000?o????\018Q????'\008?{\014??\008\006EE?'?USp\
3675?X\027'?\
3676?jNV?~\023?y?;y?\000:\004??()<v?_\\\005'\012#???????e5????\006?{?\028?Y#???a\028?S???8Gz?$q??3\023\027???????y?{?r??;?.`?\004?\030SQ\014? ?>?\014²lB?@???\015x?\019Q\"??\0158?a:.?\011?\015??????\023s\012\018]?J??\
3677Q\
3678\028$\
3679???\017?Sa\\??W??:\028??\006????\029???\022?z`D??:\017???jKAU?*\026\024?U7\"o\023Y\023?H\022?\001?~?Y??L?\016Q?\" C\024c?\005x\006??\021????L? F{3?\026b?|??$\030???vr?,??\014\020x\026G3.?\\\009F\\?t??\
3680J??@???h {?|??\005??\001????4?\007>\012Q\000\027??(??s?<?\015?o\014??Yu_\024????Zi??gX\009??e?\012??\025|?\011J?\023??2\003T~\026??4U???H?%??s&*??\003.,x??=?ZS?FGzUg?\007N?2[??=?i??~;?\004=?Q3p\028??,??O???y?;{??2?e??E:pQ?K]þA?Q?5??\011I?=s/R\007\003?_K?@%1H\005??^V??l.+_??b u\002q\018\008b:?[u???~??k???J?\012?\009??????9a(???g]?????^?+?d\001??\025J??h\008DA??UR\027?!???),O\001??!U?7\008D???;??E??\001~XY???\016N?'\001B.t?\014DX?\004?????\017?\015?~?`????Z?????W@?@?\015?.*??\015\027|y?\016I?\"\014?\024/x?#;?/??^c?B????????y??\020?9?X?q*0]Z????\026qp??\009?C???????\002??U=Z5z??)/\\?S??\024U?\000\020:?G?L?????\004?\003??\022\026]O??\004?\127??j\026???]?X\004\028\001?.?\020|\025??c\005\017O|K\014v?{?5L\015\012???B??~?o?\007?\001\\\009?#??e%CM??+K\003??\016P?o??6??t7]?\024\014?f?\022??\005PR????\030?Z\025??p??/W?\026?}S?o?8H\021?I?c?\009\012?}YI?%??K?\002B?\007E[-`?z@\021v\
3681?FP\015?\019??{<?wf=?\019??0\004[\012K??dl?:'??rC??=????W??)?\018??\024??TD?ye$???S???V?\018k??05?\002iL??@???\127??x??G?m]?Y??\004??J???????? \001'??A`?l?e?\009\028\009?$\
3682$\021??? ?\\g??|??8???)?\\v????sR??\020\009?)???\020?*\018?????\028?6j%?xb?6?i?h??\\.?b????\020L?\015\006?7r\016???`\014??\028o?]???R\004EL?\030?R\023?b4?\029??\023??\021\001'x??\\?:\026;T??n\005Tr\026?p?2d\007?F????Q+?4??P?\"?U ???0??1\"??P?\025?\031<?9$\031Y?V?=?????UF?p?\024I?)?1NZ\
3683\015\015hy]?,\019CF)k??????zx??.F]???\011\018?Vd?:\024?\011~{\0242?\020??*W\029'?BRj?~???\018?tC?!??p??\023?h\001?U??9e?t4?3\022?o`:??\007!X?'?<?\
3684#???\024B?\003?O?D;P??\011?+??=\001(?e??5?]j?_?\016a~u?c;?y\
3685?f4(DvFN\0013?!???\021\018:p?R\022\000\0186c\030K\
3686\000??\021??\022???\014{Z?L[aGIl\017??kqBL?kt\019m\020xj?f\\?\018iu???\005??u???0\009\016|??\\??Q???':&?I??BV?E?WE?!?N\029?Z?\020???\004\030?d9??2x??i??o@jP@?Y?\016b$?\020????i?\005{?????_]?R?0?D?(?9\017sW?^k\030???p??f\021?0\
3687?nw?6\014?Q` \016?lc}?}?Z???Ah?\021?=??)?T???\024?@?\009?\025?&?n?u\006:?M??Me\026??Q?;H?\0049\0013????3\016??yxH\026???2??tlTE?\023?I\017??_b?F?Y?\016\001\008\016?y\016f\014?@?\030ZG\020?rbI???fY?5?k6?K\
3688=?????f??qx?7?K?f?q\019\004x?C?Pc?2?\027??W?\029c\020??_?\019?\019?b????w?w??<\
3689\019%?\
3690????\006?W?\028-?\016??(\016X?$a??p??g?\001?C???f??L??l?\024?s???]?f?f?Je?Cs\001=?C?,?ZY?J?9YkU-?\021?kL?T\008\127{??m???\017u?O?bo???qi\028L\026?G\008\\@?\001?;??K??\030P???\027\031?M?@\005q?Z?\003?\004L??`\003?I?2?\022??\016?\008??+?#\024\011X\018d???\029Ã [F?????\023g\031??X??\"C?}??7\003?\020.??\000\031\001?3?0e'??b? ??cg??\025\009\004V0\018n\002\012\007?ydUH??QF\015=??D??A?Hr??\009C??T\
3691\000??\012\015??@Hz`?\
3692??#???.i#(1??;?\016??\007\\\029.\021??\127?FO@\
3693?3\007r\021??Y\021?????B\
3694??GY? \012?\029???;we{??p?Y??\024xl\127?\008?j?Ms?*?|??1???U?AG?\024\015??\0013??H?z?? ~)???^????\005e?TZ??????a?\005QG3?\022'?\028\\?3??Sds\018\026\030??\002???B\"?\006h ?5F?E??M?q\008??brC???\001??????V\
3695?P?Z???z\028\003?\012?IX+\023?mv???\030K??)w?PEvw4??W?OA?\006\030\012?3d??\020?7q?B?yO\012\021\022)??X-6?\023?<??F?q?D?\007\021?b\0022?&?U\024F??\006\009L??@7??H?????*??Rv\008????w\016??4??Te??g{u?c?k?\030?\009s#cO??D??D1vT?\"??\011\015\
3696Pl\
3697??6?[???\016?[\001?\018GOc????\012\004Z??l?\021??\006?.???\001?O????\002!?\006\020?\021?\012?=????*!?????*;??$?jQ????uj?P\016\001\025?\003I\024\024??w./?\018Ue?Ã…e???\127????q?-\008???5?\023?\009?\\$?\
3698???\004|?P>\007??t\029???F\030??f\"??9?\\??Z\023????D+\004?SN\008?^?Z?F*?\008Gj\003??\012j=?<??J??8?A?\007??=\017?S\
36997?\014?{??G=?XdPKd??x??\008?L???\028???Y^\009?\006?q<?RFc[M?k\
3700\008??J??\"??\003OM\025TvNsw?YDf?W\0025??E???\006?n????K?S??\0057m?\0228???\006C!?\009L??k?{??\009\023V?\024????B.*?#+v??#??:?H*???\
3701??\\??w\007?~?\029\012X\014?\026:???\007F?H????&74?l\016V?Q?W???\127? ?^?P????\\??\008?lO?+P?i??p??j?\001\017y?1?tB\022?B?.$?%?\020Rb??\
3702;\001\002?7V??\127?????\014????N'o????\007?\024U??~]?>?b+ lTc(>z\026[+?g8xm???K?:bP??H???\026~???\002??Q?F??$?\027?\001i*??(&?>}?D|\002D?4_\"?\029&?E?4??>?\
3703?\016???>\030-?;b?8\0029-?\018clr??;?T;\127!\017+?TW\026l!????<r???\022?\004?jJ\021?v?\012$? 6??_??a???R\005q8??u\031#\024qi?0?ae?3LA??\006o+\004z??@??,??1Xi??=Kr&` \015)\016\002??& _D\004?W7?\003?=?\012!??B?V\009[AG<???!nO\0311??w??p\0183+4?N?v?Z8b?\021?\025????p\025\006f??=?R?7j??????x\014?S\004?M????p?'???T???C\023\026\0233X????Xn\"?T-\029v,H??\
3704???s???\021P%?\
3705`+F.?f\008a\000R??\019?P???\021\
37060?\004\0289?l>)u\018\008??\000\0053O]??X?\028\019F?n???'V??)????P?\021??d(G?\008?)?????\127X?N\027??\006J\016?I???\026?T?8?=nhB*???md?*Q;*@q\031\012?\
3707?\006w?!?\
3708?E??zU?T???c??\021{??-?\127?S?^K?Ti?4Q?a???j3?i??r?:???a???\016?~????\029d?f@b0?x?\003}????3?sY?T????\021?Ar??tXL\025ES\029Z8??\"??\018K4#?d\017?GtB\021Q?p\\@?nM?`?d8????%????A?R??>??????x%S???\022?P=?kcM?=??N\
3709??????\029?E??V??\012?e?t??\007%?b6~?<??-????W???%?\"?g???<??C????W??9????!aP??\\??R_?\001??0(e#???\016?U?l\000??\029?E??R#?\"&\006?cKl+\014g?i?2?5??\0063?\006@?Lh?/T\018\004?\029?>??\0012a?Q?w?\022*{\022?T?X\017{?O\001\006?I???\006?I?^?v?JJM\026??k?\\@??'?\016? ??\011??f??\011x???eSV??\003?\028??\006??x??sN#*\028?????\0217?\012?a??v???o?y??\020??7?/_?(1??t?$?>?\008:\003???9???U?C~??\020vc7?q?z???#???p?\000\
3710?V\021?\0039?\014<;??\008,??b?!kP?t??}??\018(&|?????Y?0s???\004?????????\018ISJ6k?[p???q???U?&?l?d?4?Mb9?N??jq????\
3711?\005?x\028;??\008???\025[l]??D&\001?\008bM???K\022+z\0081!\
3712E\\\019&\009 `\009??.N????O?\022I????\0181?\020?3b?=??,H?k??l8?f\028[u??\004D_?\021???K^y`??\0161?t??XVQ?J?z?\025v????t]?=\001???NHe???x{B?+?[??&\026j\030iY?+yW\007\003?\020v?\009\0146?\
3713???c?D?\023??7??\017??\000??e?\004?z\\?\009??s<\000?6z?IO??\\?j??\018??\021??z?\018????\007 )?Ns???\001??5??\007??B??????T?X?\012?I?\025?30?\012? ??0??h?:??\002?0??6\012?\014b`\015??,?\027\019\004e?7c?2\020?%\005??\016?\015\127xqG\004?\003?? /^l?Y??slS?S@j??\031\027\007??O?J\023J/:??2P???E???Y?\016i@\021$2\018wKr?\028??%?\017?^B\023??e4?5u?\021?s????_\0062??U?d}\011?f?]??3D???K\017.b?~y??>2?????h?\"N?3???6#?\014? l\001??X????\"L\012[??|??s?X2 \009?<L??k???S(???????$]\008??:\025j??????\017?\031?d(?\025uEzd?+??0??e?T?L?gW??\000?j?@\011?P?y\029?o?}\020\009?VD??]?\007?7???U\029?L?{\024;????#??'J? ??\001?.?V??X\015\015????-X?S?x9\017<\017[??2?EQ}?:)-~??L?N?lQU?i0???8\026\022P?Kh?(Lo\028?C?0?+??DMDn4??4J?3?voa???,\015BxK?.{??,??????\007@???Z????'?\004?`??\004?\006@??X?9O\023/??.???_0)???'5&\
3714J`DY@\
3715r]??j?k\028?&#???Q???D3?\030??MY=??w0i?^?G{?Y\0174?3??ucg??o?\029-??\002?M??ya?B7C<???x??v?~???H\0272????????:?\127?)?\026??????c)?\028?????t?\0068????\011\025K?u.??+?\008/\003??9???>^Yx\014\009N???'k??N?y??\024?vk<??!I?2/\
3716?+l\"???f:c*\004\028???\015??\011e?\020\030\000\\fGm?\023?=5^Xba??D'B\029\028??a3?\000y?6????\0112??uex?\019?B(?e\023\000\008B]\
3717q?<?/?i?3!<?\015?i?-?c?S?????a?\011?*??#\020??G\\?\000???'Q????\020[\008X?x??)??\"?\028?F-?3??p?X?1????H?&CF??\026x\016s1\027??\030?*\
3718.?1???%m?U?\009??sY?\028?H\
3719\\=2??K??;F$??\\?\022?V\009\\?????\003?|?ud????J?\012W?1???\029Z???\020D??2??\025?????Q?H?x?\156\030J`??w?G-18?h?Y???G??e???)?]?\015?LZ6?\023\
3720?t?Y]Y\024?F\006??\028?g{\0220Xd\005??T!??Y%{b?i??O?*=\021.???\015\027Hp????\009???\022y?\011{2?Yg???@\009?6\127U\008?????DrB\017CB?X?R\006???B??\018F?\030???\0119e???y??a??]????Q?aJ[w\027??\021??~V@?-X??n?ZLE\003?/O\\\"?>???V)?j?\024-?\027:2?\005?$\007?(`d?;???7?3?\003\023???~?~?\003??!+???'f?G??`???????7????c?*??\007\024=x??\023\003???pbJ???{????\029|??p{\0304?\006????\002o???\022\\?P+\019?\025?\031\016???:?d?F?\020+T~???E???(??3I?L??W<?c?b??w?..o?@?tv\017???Z??'}?.????u?[?eB??E;?\006\022?\001¸??~s\005?\018*+:\006\027?????$6\012?i??b\012????\017??\027zw?\029n???4?o??6-5M??o??\027$?? ????OC\009[??9X\028*<??ar????'?`pa4?\001M\005X)R?w?r??G?\127?vF\024)N??\018\020A?O2?\015?h%?\
3721|????\017$?\014f'v?x#}\031??+*?\023p?IB\"?E?x+?hxe????@e? ??U?}\008\020?f?Dw_\011?[,?F?F&??!??\029\019?N\027?(?\024???5?u,\004?G?WQ\015:????\026\025???\014?t?P??\001?\006B\021?\016??*?k\023\012\009\\P?\022!x\014?$?\008g\018,&???\003?\004T\127?f#R???\012????2A#N\016g?F?EB\007H?\030?\021JJ??'???N\019?pv?)c?+O\007?K\025h???0?y\006???\006\0061*G9>??\003_?I?_?d???\020\007?|\018??\021???N??!d7*?r?\009????H???_?JE?\028H\0214???YD\
3722y9???7??A\\???v??\009a=?gM\027???\008o\024?Zj?\012?\017,?\024j??[RL9??\008?B??\012W?o??\005w?\024???m\022\003#?=?\012Xd?\020\007l\011w??j?\027V??]HP?9\\?U?f\016???-V??k??\029????\
3723L??????f???9??1?s?~??|?0\024?\022?fEyl?nLy\007H|???-?Q%??P?sU?,\017???^?x???_?!B?2\014????cM\017s\004???@?DuPq\031?9??x??~\004-?\\U?,\021o$~\003!k\009G???\005?h \026?{??\
37247B?u6?????> 8?%L??%??m?3?1k???#Q??!\027@.pCP??\001\127?Un??????~??b*?<W?\030.3\\????\024?(-K`MC\\??Q?\000\021s6}??\007\027????c?D#?d??t?e?I?]a\022?G?\031\004>\
3725g??????x??!o\009\007???\022?G??C,+?/?T?;?????$E?}9n?i??\016?p\031\027\023\030?H?<?Hp\021I??\0178e??d?'F??>\026?F?\025|?_\029?|<zb.?????\018\016?n?\
3726??\024?m??'_\016??>l???\024\0007??? ?.t???\029/0?y?}?n\019J?qw\022\008?H1z.i^??`?\004?`\023;\022B\027??\0168q?\0193\006\001?H?y???;\026Q}\014???\007?\019????|?<\012B\014]??<\022?p?K\028??O3&??v`???e?\017?Kj?\\iU\017Tw?'\026\004g?c*]\014 ??-{?dg??K?9\005???Ej???*v?\022??)?\001\014\027? ?\137\\,??m\026n}??3?pt\021?;eg?\012?????F\018?a??C>\007]k~`?u5???\014?K\025BH?<H?JZ[9??\030\029???#?'???\"?T??;?\024g\003Z?????e\\\"?n???\004?c#?\\?;?\026s?9\001\003??V??h????;????\
3727f?5g$\009??\127\"t?i(?@0C%B??e\016?????\028U?R/?sy?^Qo#\011V?q.?Ev??*=\029?2???._?V?^?D?g?\001?g\029$\0004??A??(?s\008^\006?\029?L??r?Hx?UQ?b????\017?[?r#{?\004?0???e\024?0@???\005:???????KF?e??*S\0082?Ek\"?\
3728??}?%??,??N??t?0?N\
37294???m=ecc??>;??/cA\000?C?E??U6\002?\020?Z??\
3730EZ?F|?(W?\016????f`\004d?O?$??Mr??Zv??S??\003?r??B?\
3731?l\012j?M?RV-?\006\
3732Y?|??>?b\021\009p???U(?#?B??\008O~???\000?+#?*?Q??G???%&??\009??dk\021??&_kx]?2?*??0!????T??????-??\027?\029??V??\000?<J?8N??????R?[??$x?D?76k0e\005?5?h9?w{??i??G\001\002?zQ3F ??\024?\018S\025qC\003\007??=?BV[??+?\127???GH6?v\127?u?'Y\011??*?`=\026??o?\016??\\\008?\022*??I???????&Qc???iM+.9Nv\
3733k?N6M?J???:+??j?|???\019U?Bl*\"???'?ZL<?J?eS???~p:??g??c?2{? ?j\002??V??i?????r ?_[W??r\006?Me\028+?x?P?m??PG?\005?????:\027j\014X\0256?lu?]e?\026\004?B??_\005?\009\027??HaAF4!<\
3734;s\014???O_???i??4?+O==W??T??\023?~??Z~?Ròx<:?>?\000?\029?ry?\"??©_?Id,\004#?A>\127????)\016??\0261\011?;???n?r???ae?\001?\020?V?h?kN.??g?.r??\030?:??[???????~#iP??cw|\001?._5I?\\>\0002 \003\
37352?h2\020wNu??\\?\008??K\027?r?PN\026?*\002??FT?2\001?F\"?-|\023??G?p\003 r????\007???iLry>\028m?\012i??\009??F?1V-?g#a?|C??f>?8?????Z]?qZ@?\006?)\002?w?%+?F\017??\023\025&?\022<??] $j?A\021x?y?\014??8?/w?R?B?\
3736??????X%?dp????_`?;FW?T?\025,???&[?\127??6?@lr?5?3?r?�??FX????\003???\016\001\030?-\002???n???W?,v\002\014Tn?????}?\000\127<?~?\000%~?w\000F?J\016Uee?\"(?Wx???i\006??{\007U?Cr????VO`???\017[G?#-\017?{???/\0147t??AQ?\031??¾???t\021??uz~`9 ?u\012?>?]???\008?\007n?u?1??yr 0\004??[@??'&?\016?N\002\008\006\",+????x???\030\025?Vh\000?O\028eD??*s?\009!\005\
37376Q\007\007?V?????\022:?g?_?!F???W?s\0307??dqX??_???)\019?T??o?rF?\029A?4r?>e?Bw[?K?!??C}?i??.?(1?Y)DI??t??R??k?????Nb??9?!?\012?]??)?q?b:\143???G?{H??0r35?|L.?.\021]GR~??S???E??3??a>????%?\\3\006\027?n\016???b?*?`5?5fOKo#??HgH???,?zf????+{)L???(?n?B??`???m?l?Ie??Y?\026\015`^\0044\\m .???????|???{??\\7?\023?_\030=?\001?6\008?T?Pp2\017?%;?o??\007 ????1????G?\021?h???f=\018K\030G^?G??M@.\0097\025?r\027?X????\029??^|@J]????a?1?????h???g?\031\008?F?-??M?\"?P?#A??\0011??S????=+????\020?U6???\024\029\028peR\127\\.&??w????8k\025???\020\009j??xD?@??D\026?\020?o?\022G??\026??\011?\011f\007?%u-$?N?\006T??\005?U?/F?`\0253P\003????h9??\017? y?N\
3738H?\011?6??a\\???\007?<\
3739\019?8??\009À\
3740?.??L&?\\?!2??V$\\4?\017?????z?\019?K\001??u?\022m??\022?3!?\002G0Z?z'?\023??5??\012??J???????Thi\005e???K?a\031U?=\000\000\015?,?????j ??\026????h\012?G?\019?(?D?6\004???)9/]?????-?3?8?iC?????fI?????L/??j??\000?<\027\0012)??p??*\027?????n\008??j?\016?Qb\008???B%\012+\016\012R?\029a?????l?-?\030VPc??????]?w??>?$\026@????P?????\
3741???c????,s???uR????l?`@?X?y???#'\"}U?X???jN?Q??iS5q??y??\009??:R?\003??k?x?(?n???8?|?d??f?B\029??v.'K?&;\008K8K??/\009V??h?,fg?????m?6;oEU?\014?0_\022?-'?\022???\019|?[\008b???P?(?#?=R??\
3742\027?r?\000???2^?y\"????????r\019h\024\028?,N H??\005?\006]=LS?X?S?4?{???*E{?2????0??KSl??\016~t\
3743\031?|??\
3744v???V???\028@????GE?>l?????mu???|\009??????A??)\008\003@\020@? ?Rh?\"P?\025?F?os\
3745}\127\
3746?\001]\001X\008\014?J\019??X\"pNfL?xW??\001|e??\
3747\023?pB?\028p?\"\012ns9\127?.\021\019S\
3748?(?\022?\009???8j?E???5\005\031????\000?\"???g?{????tq^?\020\
37494??49?,?[x??[?U\011??T\028\004<<Z*??\007'??~\025-\"\030?????^??}?(S,SD\005E(?|???\004???????\009???\025p?}\009??@?3E._f?G?\003oT?\006\003?w?.V\\?&??h???zx?\014\007???X\025?QP(/J?u?\002??n?eO?5@?vq\008Rt?2??:g??Y???#?5??w?|'Þ?\012?rEv'qh?????\011????G???\004;>???VP\"j'?A\014?z5Û?\026?\
3750?y}W????Zd}?\012&??\\??9???*`\012\017j\001;<??y{?-%\0174?x\018\004u]?\008????4`n??w?C???\
3751???c?~???,'?\019??t?L????X~\022??v\020??o?vy??\005?????{\025?????????g??Nj45?wW?&]?}\0279????{??w\"?\0192\127??a?*\019???#??5????\
3752?\016T\011\018????2?q\
3753?\012?'b?>?T?^???\021K(Of?E?/???-?P#\000)???#:T?????\002?mt?_?(l7A{?`w?\
3754}?#?\005\021??k?]???\007D`?\030??E??~c??f?\005\002??xbnD??>???><\022??K????\022?(?{?\022??+?'???i???t?oiE?|??`+?qm?F??$???y\000\020?v?\
3755(E\020pG\002\011?\020&?~??\005PG\006?Q?*?q\127??<;?\017\008;\008??;?W? \011??B??[??>+?M;h?\028s???\
3756r?t\029A????????|R?\004?\030?\025o??\023?j?!g\028?!\
3757G$^??\024;\001VQ\006\015?'Xj?F?h\0225?4?\011??\016o??6=?\012???w?p?C??c?{?\015'\015??>%|U???J???=?\009r5\029#?Yyc\017~?P??\030?\018???????!\026\
3758??qk?]??\027?B\031(??\028Y?#??dd?a???\012?\\?\
3759@8:\002{?\\\004???\004??\019\
3760???%?\000?@??a!????h R?:[\\3zsO????t??q???fo?>?,h=\019?K??D]???q?ZaS???|?g????\021x????j??\006sO\031?!@?5?:??:TM^?r{?1??I?????Vz??6????Ã’?\025??q\014?2mK\127?nYC?????E\009h?OV?b?\005?rx??\030jd_?~?\127=t??A\003?\"\027?E^aC??|C???AK?\"\024C\026\027?e?k?u?x?\024?H~????{??p3?~?H?v?O?A??? ????\025$?m?\
3761l?\031???r3?e\
3762?????{8??T????\127SD\023z?m?????n='J?*\127?8T?Â?;?m???]4?????V?\005\0261?N]?D?m?????f?0???q????iS??L?S~d'\018?\019?\029?8???d?\029???EV5?\025\015EqU??Q???\004hl?\\?.Z$??n?X?u?;?\009(!?1\020?\021?\026~?r2???\000-??\022???l?\127???\018<??m\"[\016\\??l?T/?%?;?????qu?>u?j?!?R~F?\022???)\014?N|c???N\024???Q????\003\005V???????FM??q?;????~??8a?\\\009??3D?\"?#\
3763???r?3m?\002????t^?f/\002?v????o\029GPiy0?P\009??CTv[\127M?\017?\022.\023?Sm\0089?[\015@???g#}?\018?@?/\022?)??\024?\000???9?+\028\018$(??\007??\"\016??I`?<\008\023?/\"?EQ?3\003\028?D6?q2\028?\012?\030T??N)????S???(??\012,s??l?K?\016h?\017a?,?2?MO?\001??Pv\023qq0\018W?6@??\021\015?I\020?v?X???qd6??$j??z??!?\\??5??b\000\"q[{s????tmqa??\016\003\021?i&????\0126h?l\004i?eAi\019\015bP?\025?^/??i???q{?p??Sr?????QZ\026\028?L?-$???#?c?U?t??\023?G\018?(??D\020X?w=\
3764???-?6?\026??]?q\006?<j??k????????5=?|??,6\000:f?^,?H?K?\
3765???&??o2d?\005m?h?H?\011?%t??`?#??w?\003???{6??V)(?p??a\016z?)?????jd?_?\014\014??\007?vD_?????B???\017??\025?J$UVg??\026\022\004??\\=?;\014($?\014\031??F???P?h??U\026?????????v??!?G???M?#'\001?w??M??p?\027jyEw?n???\012\011ulS???}?-n`?\
3766a? ~e???ó?RO????\127???\017??\
3767^?A=h?E??[?T?M#??7t?1S??}C?7???\009?oG?\017??u???*9uKn?Y??\007?D?V[?Q??????}\127Æ???-?e??????^E?\0198???!\02329/?????\017?\"?1?&??w?5u\003?U[m\019Xo??n?b???\024~{6?M??H\\mZ??ox-????\006U?]?h`*[?Y?.??\007?[?:?1??\023g??\001?\026??@b!???P??I#??4pZ???\028??mc?i?j??\000]?]d?a`????\
3768\005'2\020r=\005??=\027L!+b??9??b#?\025????M?b?b???\030Fo\008?AK??[?qB\027.\027^?I????\024?\015??dY?????c+>?A??2?ß?\009?\"Z?\022?#.???_\"?Wa?\022p?7?`??j?Yf0^??F;???]N?5i\
3769\021\"?\127|A\007Xs?\005?????8??,?k!??7a?\018?m?Z?1\017?7\023???\030#?j?\017??\005??\007??e\0275{?b?m5??KJ\018n{?B2??T??\005\028?p?]\018.29Cb??-?,j?0?\"???v?h??\004uN?u9??p#?^?&?&??9?\014?i*?.p??$\029???7>#??\006?[?\030~D?K?u\023??d??1a??=07?????0y??S???Z}?\007??}wz?\004??\"???j???+?G?jM$?Cv2=??Ms?\003?D?f\003?Y?v43\015,J????h.Hh?\000z\009??\031\020?r?\023/]BJ\018\012???.??{????.?x?t??\002\
3770?\021?\006??)3H E???~?m?)*3?R??xK???+\\??\\\"??l??y?v@?0?Q\018QL?\019?B\012?.?\127?z?)???????\020?u?)?\005\006\007\023??e???/?d\"??\007??a?v???p??Yw????\001?F]\"\003)*\014?????~?t\014?\027?(\029T??v?\\@<???\003????r\001??whX?1O??0???0\0061C??t>BJ\
3771?`????3(\023\127\027\012\006?\
3772?N\022????.?p&]\025?\\?p??\000?l??UqO?\018?m0?lcOd?F??\024\"Y];?!\0269??yK?\007\015?k??????~4?????P7?A\002?N\007?A?a?\012??n\031[???k???u?n\012\023\012P??)?\007?????#?{K????@?0???N??9?\029??J??\028m_??\0237x?T0?????D\016?\018???e?f&g\\?7ñ???T2?8+V(?!\016r\
3773Q?<\029??=??t???n??z?pz?z?õ?>\025?\
3774P??B???D\003?OK??Ù?????)?\"K.7?Fp?\016\001\024\008?\0167?I&?5/\"??c???\127??x)!?\000w\009?^???{?g???i2\023{\028?????a??????'?\019?/??!)D???\031???j?Y?\012??9??@>??\021?Q]??`k/i?\0049\030\019???^?4??\001??xb?????$?h???+?\003#??B,\014E?W?ZK2GQ?W??f?=??]]\
3775\028&?\000???h?RB\009\021m??\002S\\??~?r?'Nt?Ev\012?@Y??&}?4????nk??\031?Sm??Y0?,??\006Y??K??O???\028+\023\029V???5 ????\
3776\0201??\030???>\019?^?\127??\031C?\
3777O;?????????(S)?$|?0D5?I\
3778?\"2?????=??u\"??B?\127?h?$??T????%{r??t?,\017?H????8b??G?'??\024??%?=z??n?g?a?K\\?Y:]\012xGC??\003p't?8a\
3779???s?#\021\018?\006y%?\027??\007??kX\031???Jw?UT?U?ekR?U??)Wyiu=)-?}?g?\031?.?\007\008?L.\
3780?J?,?Y?\\?j\001???e?)?(PgTm\021\009?_\015?\016?/??y8\024??o8??,\001??dif?`>?)??\006?|\019z???6?\000eV?$$?|Q?G\
3781rea\023ef\027[y?kDHz?9iZ??????l?Z???\\\005?\002?\\?`+?\026?\030?\0070\002\011?)\
3782?M?r5D9-?=?\020W{???z?j??_?n??",
3783 "x\001+)JMU?0`040031Q?K?,I,))?L*-I-f?????\001\002??\030\006L??C????&H*3???R\025?j??-??\023m?z??J\
3784??Jm\005\000?#9",
3785 "x\001+)JMU06b040031QHI,Idx?L???\015????V???l|%w?3\000\023?\016\028",
3786 "x\001K??OR05a??RPH??O?.?T?U\000?\020\020??d?\014\\\"\002.a\002???\002\000?/\014?",
3787 "x\001+)JMU04?`040031Q?K?,I,))?L*-I-f?????\001\002??\030\006L??C????&H*3???R\025?j??-??\023m?z??J\
3788??Jm\005&\006@?P?ZT?Z?Pppa?\0197???W??|;???O-?????\002??U/???v?c???6`F??&?\019\0009?A-",
3789 "x\001+)JMU046g01\000\002???b\006??S<?z??]?8<,kr??y3\019\
3790\
3791\012?LL\020\
3792??r2?K\024?oD?L~?\020?U~?&%?\015Mj??\016#?\019?R?\025?|9???j???^??[???}l?\0015??8?\008jJ???o???0????M?v?9\031]\000R\028;?",
3793 "x\001K??OR01g??RPP??O?O?T?\001?CR?K ?\0188+ '?2?\008(Z\011\000??\014M",
3794 "x\001+)JMU06b040031QHI,Id??+\020?\024?R??|?s???k???\000?\012?",
3795 "x\001??1\014?0\008\000@g_?tv\000,?~??@???1???\011????a???{N)?Æ_y??B???H?WnJq?BgV\007d]-J???\009Qwd?\029? \000\020?g??\002ZV=?",
3796 "x\001?Y?o?6\016??W\016~r0[?X?u\0032`h\0317?hQl?a\004?D;BeI??8I?????\")?R???\030???xG\030?~???\018/N?N?Y,\026Y7????&?zS?s?a\018L?4?L???T;?_v ??z??i???>?{??WE?\017?zHd&\027\025?1/??Q??zi#?2??d?DM??u\025?s???A?/?,1\004?2+???|?LH\030--=??W?S??.O???H???)?;^V??\020??zQ\022?\031?:\030/?G?q??'?I%?\\6aQ?|6m?rz4???L\
3797?H\
3798\028??\005??????Y*\025??F?@?&\002q~N\031??t%?k?O?????M?(?D?!?*?b\026?MQ7X????(\012?s\024.O??????\016?\004?>??@?B??\021-???*rbo?Gf????H?4\018~\"? ?\025D\025\0191??k\018?L\018??\004?8???v.???1(\000?c6^?\001c??r9?]?\016K\004??Okf?)?d???e?_????f?Y\029?r?FYz/g?Fd?\023gE?V\009?E%.?,?Hs??QZ??-\027h????L????\029N\017k\018s?<\003\006z?\006?D??\004&\008\024\004W??{?FF?7-I??\000???\0153?u??\022Y??Y??h?BVr\003??D?n?????|?\0072?\
3799dvu?]?\127B\
3800???\014\011??\\4`?\
3801\002vS?|z]?n?\020#??\022/??\027y?\025^}??Z?QW\017?C?9b?j\001C?h???????J\027?kS0@?cVC?QF`?2?|??L?l?&??W???F?????\027\007???rfM?w\020Ch=e\\?r_k???\011?\"?k{??X??s=*??V\0012??9`X\003$?!??G??F???M?UG7?>\006?\\%\003]n?\026?xC??*\009j?2?FD%o?\009???????\007\002?p??????????_?\
3802\"?'9??k\025?'?,??FTH??OM'??k\002B??7\000n?????? \006?\028V??0??\012f???\019??~?\020j?\012??3-??h;\\t??\
3803<??Z?L?E?B?\025\000????M?\026?f?4??s\029???c????\\?????%??S&?1?{%\026J??w(?e\"?&N?\\\020k*?Z?Q\006?????\007?\031?j? ????\
3804q$\003??xs\001?\020?\021d?@??Jn\0114\003l&NK?Ç??lwu<?v??r?*r??\006?p???\015???\031w\018\024???=Z???P?+=%\002?$J?/\0277n?\0148u?gG?\023:j???g\007\012n??t?#G\028\022YB??[?????\001???O?q?\017?Q7C\018?Q??\026?????dP\
3805\024?\027\019A\000++\017?\017?xP?:???<I(?A$?v?\018?I??\
3806t<O???D???p\002v?\021<rz\\?h*s=?h+rw*??\007?^??\028x??[????,\020\
3807?@?????\\?if??X,??d?g??KOF??\027#??\015\017?Q??j?I?3\031?KsA4?\021#??????[7?j]?h??#\004$???EoYMU??u;??\005W?>Rv??v??s??Gc?G?c\\??\021\011???+\011\024\031P\\??t??)?\028?\014=ZT,wG?@??]?\016\030qYg?\\?(?w?.y?T???t\029???\028PX?i?B???\025ah??FT?c?Z\008><<hA8??+??R?/3W?z?h?A\
3808?4?v??XRDs??\017???b??i??[I;?E?l???\026\002;\029\006?tF?????\003\0117???M6??>Y?\025\026?\014B\028??s??D4t\030??9R???QF\
380998?\021I??\026?\029\030)v?|?\020s??\027T\0315`?\031M??@?\014?\127{?\0195??????N\"??!????\
3810?Gz?6E?Aw?G\024$8L?.?+>`??b??\016?8\024?I5??\001?\"]????.\015?&??\008?t9????\028???B}Vfp\025,\022?rP\011>N??\007x/?#?n?cm{iq???UU8LOV??< ???\003???m?n\005T?J?+??\0150?\025?R[??~?{??x;???a?????\0093???\026:?x\021`1h?????D,~\006*#\012\030\009?W?3&???;W?X\026\015????a;_?9\028\
3811??\006? dRk!?\002?;0\029S?e4??2??~'???\019?s?ohY?.?\000\008>M?j??\025cx\\?UJ\006??\022?\020??7??\023?",
3812 "x\001+)JMU03a01\000\002??????\"???W???x?r1x?\000???\012L9 \
3813JR?K\024B?????\018\019?Y~??\026???\000\000?\
3814\028T",
3815 "x\001K??OR026a0?\005???\003??-???\000djN?",
3816 "x\001u?=\011?0\016?]?8????.\029?????\"??|h mBr??7U'??\028<<??;h3?f?[?\015d?\011\018???L ?\022\030p?`???pha?\027?z\")3e??0F?,?:x2#D\023.?\007??Q??dE?<,Õ?\021?wtv\009t\020??\016_???o???\
3817[\127L?K\019?Q?_es??<??\031????????>?-?I~j?d\031G2??}?\127\001?O??",
3818 "x\001K??OR???`H\002\001\005,?\028?\024D?\011?\007\027?G\015N?F?((P9\012?I\007\\??0??\000\0004?6",
3819 "x\001}RMk\0271\016?Y?b =\005??P\
3820=?\016gc0??xM??V;?\014?\023\026???}G?\029zit\024}??7z\026\\\028????\031??\023?1?2?7;?-9\004k?6??\007??p*oku\005??F?`c??AT??/1i\
3821???\026??\
3822?\002?#?p???d??????s?p?\023???\021pQ?\
3823\\?s?^?????\012\018=S????/?????K??¾?~?oH?)a6??\014?#\023??l??\003UT??]^????/1??)9??<8?<?O\007?F???\006C??~??'?:??\020?\
3824???d??+\019B??@??Ct?\023?.Z?CY?b?O?_nn??2\030?G??\020??d??\006}????J???l?\026??\027?J??d\003W=\029e??\007!??\008#?\031G?\025M9\025??K\002n????y?xqY???z??\026?\030Z??QoZ?,??x??\002?9?\011",
3825 "x\001+)JMU04?d040031QHI,I??)?a?`\022????m????[??\029L??-AS?XÂ ?\020????\
3826?P????S?????\005U???Y?_??z#????!??\002??????*??\000?t+?",
3827 "x\001K??OR0?d??RP(?/?IQ?UP??/-N5T?\001?e???\023U\002?kA\" ?\017?Q\001a?\002\000??\017?",
3828 "107081fe84aa72683e0a970bfad56ca79c1ed2e7",
3829 "7777777777777777777777777\
38307ccccccccccccccccccccccc7\
38317ccccccccccccccccccccccc7\
38327ccccccccccccccccccccccc7\
38337ccccccccccccccccccccccc7\
38347ccccccccccccccccccccccc7\
38357ccccccccccccccccccccccc7\
38367ccccccccccccccccccccccc7\
38377777777777777777777777777",
3838 "{\
3839 blocksY = {\
3840 2\
3841 },\
3842 blocksX = {\
3843 4\
3844 },\
3845}",
3846 "--Editor (yaay make your own levels based on this \"engine\")\
3847--Again, written by me c:\
3848--(I know, I'm shit at programming but hey why not C:)\
3849\
3850--Variablen\
3851_ver = 0.1\
3852_verstr = \"0.1\"\
3853pfad = \"\"\
3854\
3855lvlData = {\
3856\009spawnPosX=\"1\",\
3857\009spawnPosY=\"1\",\
3858\009blocksX = {\
3859\
3860\009},\
3861\009blocksY = {\
3862\
3863\009}\
3864}\
3865\
3866--Funktionen\
3867function clear(bg, fg)\
3868\009term.setCursorPos(1,1)\
3869\009term.setBackgroundColor(bg)\
3870\009term.setTextColor(fg)\
3871\009term.clear()\
3872end\
3873\
3874function menu()\
3875\009clear(colors.black, colors.white)\
3876\009term.setBackgroundColor(colors.lime)\
3877\009term.setCursorPos(25, 6)\
3878\009term.write(\"NEW\")\
3879\009term.setCursorPos(24, 8)\
3880\009term.write(\"LOAD\")\
3881\009term.setCursorPos(24, 10)\
3882\009term.write(\"EXIT\")\
3883\009menu = true\
3884\009while menu do\
3885\009\009local event, button, x, y = os.pullEventRaw(\"mouse_click\")\
3886\009\009if button == 1 and x >= 25 and x <= 27 and y == 6 then\
3887\009\009\009menu = false\
3888\009\009\009menu2()\
3889\009\009end\
3890\009end\
3891end\
3892\
3893function menu2()\
3894\009clear(colors.black, colors.white)\
3895\009oldTerm = term.native()\
3896\009local graywindow = window.create(oldTerm, 15, 5, 20, 5)\
3897\009term.redirect(graywindow)\
3898\009term.setBackgroundColor(colors.lightGray)\
3899\009term.setCursorPos(1,1)\
3900\009term.clear()\
3901\009local textBox = window.create(term.current(), 2, 2, 18, 1)\
3902\009textBox.setBackgroundColor(colors.gray)\
3903\009textBox.setTextColor(colors.lime)\
3904\009textBox.clear()\
3905\009textBox.write(\"Enter Name/Desti..\")\
3906\009term.setBackgroundColor(colors.gray)\
3907\009term.setTextColor(colors.white)\
3908\009term.setCursorPos(2, 4)\
3909\009term.write(\"New\")\
3910\009term.setCursorPos(16, 4)\
3911\009term.write(\"Load\")\
3912\009menu = true\
3913\009while menu do\
3914\009\009local event, button, x, y = os.pullEvent(\"mouse_click\")\
3915\009\009if button == 1 and x >= 16 and x <= 34 and y == 6 then\
3916\009\009\009term.redirect(textBox)\
3917\009\009\009term.clear()\
3918\009\009\009term.setCursorPos(1,1)\
3919\009\009\009local eingabe = read()\
3920\009\009\009pfad = eingabe\
3921\009\009\009term.setCursorPos(1,1)\
3922\009\009\009term.clear()\
3923\009\009\009term.write(eingabe)\
3924\009\009\009term.redirect(graywindow)\
3925\009\009elseif button == 1 and x >= 16 and x <= 18 and y == 8 then\
3926\009\009\009if fs.exists(pfad..\".lvl\") then\
3927\009\009\009\009textBox.setCursorPos(1,1)\
3928\009\009\009\009textBox.clear()\
3929\009\009\009\009textBox.setTextColor(colors.red)\
3930\009\009\009\009textBox.write(\"Already exists.\")\
3931\009\009\009\009textBox.setTextColor(colors.lime)\
3932\009\009\009elseif pfad == \"\" or pfad == nil then\
3933\009\009\009\009textBox.setCursorPos(1,1)\
3934\009\009\009\009textBox.clear()\
3935\009\009\009\009textBox.setTextColor(colors.red)\
3936\009\009\009\009textBox.write(\"Please fill in.\")\
3937\009\009\009\009textBox.setTextColor(colors.lime)\
3938\009\009\009else\
3939\009\009\009\009term.redirect(oldTerm)\
3940\009\009\009\009term.setCursorPos(1,1)\
3941\009\009\009\009clear(colors.black, colors.white)\
3942\009\009\009\009menu = false\
3943\009\009\009\009print(\"Please draw your LevelDESIGN.\")\
3944\009\009\009\009print(\"This will be that, what you see when playing the level.\")\
3945\009\009\009\009sleep(3)\
3946\009\009\009\009shell.run(\"paint \"..pfad..\".lvl\")\
3947\009\009\009\009\
3948\009\009\009\009editor()\
3949\009\009\009end\
3950\009\009elseif button == 1 and x >= 30 and x <= 34 and y == 8 then\
3951\009\009\009if fs.exists(pfad..\".lvl\") and fs.exists(pfad..\".lvlDat\") then\
3952\009\009\009\009term.redirect(oldTerm)\
3953\009\009\009\009menu = false\
3954\009\009\009\009editor(\"true\")\
3955\009\009\009elseif fs.exists(pfad..\".lvl\") then\
3956\009\009\009\009term.redirect(oldTerm)\
3957\009\009\009\009menu = false\
3958\009\009\009\009editor(\"false\")\
3959\009\009\009else\
3960\009\009\009\009textBox.setCursorPos(1,1)\
3961\009\009\009\009textBox.clear()\
3962\009\009\009\009textBox.setTextColor(colors.red)\
3963\009\009\009\009textBox.write(\"Doesn't exist.\")\
3964\009\009\009\009textBox.setTextColor(colors.lime)\
3965\009\009\009end\
3966\009\009end\
3967\009end\
3968\
3969end\
3970\
3971function loadDat()\
3972\009local file = fs.open(pfad..\".lvlDat\",\"r\")\
3973\009local inhalt = file.readAll()\
3974\009lvlData = textutils.unserialize(inhalt)\
3975\009file.close()\
3976end\
3977\
3978function editor(loadData)\
3979\009clear(colors.black, colors.white)\
3980\009lvl = paintutils.loadImage(pfad..\".lvl\")\
3981\009paintutils.drawImage(lvl, 1, 1)\
3982\009if loadData == \"true\" then\
3983\009\009loadDat()\
3984\009\009reloadMap()\
3985\009end\
3986\009term.setCursorPos(1,19)\
3987\009term.setBackgroundColor(colors.lightGray)\
3988\009term.clearLine()\
3989\009term.setBackgroundColor(colors.lime)\
3990\009term.write(\" Block \")\
3991\009term.setBackgroundColor(colors.lightGray)\
3992\009term.write(\" \")\
3993\009term.setBackgroundColor(colors.lime)\
3994\009term.write(\" Spawn \")\
3995\009term.setBackgroundColor(colors.lightGray)\
3996\009term.write(\" \")\
3997\009term.setBackgroundColor(colors.lime)\
3998\009term.write(\" Remove \")\
3999\009editing = true\
4000\009while editing do\
4001\009\009local event, button, x, y = os.pullEventRaw()\
4002\009\009if event == \"mouse_click\" and button == 1 and x >= 1 and x <= 7 and y == 19 then\
4003\009\009\009currentBlock = \"block\"\
4004\009\009elseif event == \"mouse_click\" and button == 1 and x >= 9 and x <= 15 and y == 19 then\
4005\009\009\009currentBlock = \"spawn\"\
4006\009\009elseif event == \"mouse_click\" and button == 1 and x >= 17 and x <= 24 and y == 19 then\
4007\009\009\009currentBlock = \"remove\"\
4008\009\009elseif event == \"mouse_click\" and button == 1 and x >= 1 and x <= 51 and y >= 1 and y <= 18 then\
4009\009\009\009if currentBlock == \"block\" then\
4010\009\009\009\009term.setCursorPos(x, y)\
4011\009\009\009\009term.setBackgroundColor(colors.brown)\
4012\009\009\009\009term.setTextColor(colors.white)\
4013\009\009\009\009term.write(\"B\")\
4014\009\009\009\009local x = tostring(x)\
4015\009\009\009\009local y = tostring(y)\
4016\009\009\009\009table.insert(lvlData.blocksX, x)\
4017\009\009\009\009table.insert(lvlData.blocksY, y)\
4018\009\009\009\009reloadMap()\
4019\009\009\009elseif currentBlock == \"spawn\" then\
4020\009\009\009\009term.setCursorPos(x, y)\
4021\009\009\009\009term.setBackgroundColor(colors.blue)\
4022\009\009\009\009term.setTextColor(colors.white)\
4023\009\009\009\009term.write(\"S\")\
4024\009\009\009\009local x = tostring(x)\
4025\009\009\009\009local y = tostring(y)\
4026\009\009\009\009lvlData.spawnPosX = x\
4027\009\009\009\009lvlData.spawnPosY = y\
4028\009\009\009\009reloadMap()\
4029\009\009\009elseif currentBlock == \"remove\" then\
4030\009\009\009\009for _, block in ipairs(lvlData.blocksX) do\
4031\009\009\009\009\009local blockY = tonumber(lvlData.blocksY[_])\
4032\009\009\009\009\009local block = tonumber(block)\
4033\009\009\009\009\009if x == block and y == blockY then\
4034\009\009\009\009\009\009lvlData.blocksX[_] = nil\
4035\009\009\009\009\009\009lvlData.blocksY[_] = nil\
4036\009\009\009\009\009end\
4037\009\009\009\009end\
4038\009\009\009\009if x == lvlData.spawnPosX and y == lvlData.spawnPosY then\
4039\009\009\009\009\009lvlData.spawnPosX = \"1\"\
4040\009\009\009\009\009lvlData.spawnPosY = \"1\"\
4041\009\009\009\009end\
4042\009\009\009\009reloadMap()\
4043\009\009\009end\
4044\009\009elseif event == \"key\" and button == keys.s then\
4045\009\009\009local file = fs.open(pfad..\".lvlDat\",\"w\")\
4046\009\009\009local lvlData = textutils.serialize(lvlData)\
4047\009\009\009file.write(lvlData)\
4048\009\009\009file.close()\
4049\009\009\009clear(colors.black, colors.white)\
4050\
4051\009\009\009editing = false\
4052\009\009\009break\
4053\009\009end\
4054\
4055\009end\
4056end\
4057\
4058function reloadMap()\
4059\009clear(colors.black, colors.white)\
4060\009term.setCursorPos(1,19)\
4061\009term.setBackgroundColor(colors.lightGray)\
4062\009term.clearLine()\
4063\009term.setBackgroundColor(colors.lime)\
4064\009term.write(\" Block \")\
4065\009term.setBackgroundColor(colors.lightGray)\
4066\009term.write(\" \")\
4067\009term.setBackgroundColor(colors.lime)\
4068\009term.write(\" Spawn \")\
4069\009term.setBackgroundColor(colors.lightGray)\
4070\009term.write(\" \")\
4071\009term.setBackgroundColor(colors.lime)\
4072\009term.write(\" Remove \")\
4073\009term.setCursorPos(1,1)\
4074\009paintutils.drawImage(lvl, 1, 1)\
4075\009for _, block in ipairs(lvlData.blocksX) do\
4076\009\009local blockY = tonumber(lvlData.blocksY[_])\
4077\009\009local block = tonumber(block)\
4078\009\009term.setCursorPos(block, blockY)\
4079\009\009term.setBackgroundColor(colors.brown)\
4080\009\009term.setTextColor(colors.white)\
4081\009\009term.write(\"B\")\
4082\009end\
4083\009local sX = tonumber(lvlData.spawnPosX)\
4084\009local sY = tonumber(lvlData.spawnPosY)\
4085\009term.setCursorPos(sX, sY)\
4086\009term.setBackgroundColor(colors.blue)\
4087\009term.setTextColor(colors.white)\
4088\009term.write(\"S\")\
4089end\
4090\
4091\
4092\
4093--Code\
4094menu2()",
4095 "7777777777777777777777777\
40967ccccccccccccccccccccccc7\
40977ccccccccccccccccccccccc7\
40987ccccccccccccccccccccccc7\
40997ccccccccccccccccccccccc7\
41007ccccccccccccccccccccccc7\
41017ccccccccccccccccccccccc7\
41027ccccccccccccccccccccccc7\
41037777777777777777777777777",
4104 "{\
4105 laddersX = {},\
4106 laddersY = {},\
4107 spawnX = 1,\
4108 blocksY = {\
4109 9,\
4110 9,\
4111 9,\
4112 9,\
4113 9,\
4114 9,\
4115 9,\
4116 9,\
4117 9,\
4118 9,\
4119 9,\
4120 9,\
4121 9,\
4122 9,\
4123 9,\
4124 9,\
4125 9,\
4126 9,\
4127 9,\
4128 9,\
4129 9,\
4130 9,\
4131 9,\
4132 9,\
4133 9,\
4134 9,\
4135 8,\
4136 7,\
4137 6,\
4138 5,\
4139 4,\
4140 3,\
4141 2,\
4142 8,\
4143 7,\
4144 6,\
4145 5,\
4146 4,\
4147 3,\
4148 2,\
4149 1,\
4150 1,\
4151 1,\
4152 1,\
4153 1,\
4154 1,\
4155 1,\
4156 1,\
4157 1,\
4158 1,\
4159 1,\
4160 1,\
4161 1,\
4162 1,\
4163 1,\
4164 1,\
4165 1,\
4166 1,\
4167 1,\
4168 1,\
4169 1,\
4170 1,\
4171 1,\
4172 1,\
4173 1,\
4174 },\
4175 spawnY = 1,\
4176 doorsX = {},\
4177 blocksX = {\
4178 1,\
4179 2,\
4180 3,\
4181 4,\
4182 5,\
4183 6,\
4184 7,\
4185 8,\
4186 9,\
4187 9,\
4188 10,\
4189 11,\
4190 12,\
4191 13,\
4192 14,\
4193 15,\
4194 16,\
4195 17,\
4196 18,\
4197 19,\
4198 20,\
4199 21,\
4200 22,\
4201 23,\
4202 24,\
4203 25,\
4204 25,\
4205 25,\
4206 25,\
4207 25,\
4208 25,\
4209 25,\
4210 25,\
4211 1,\
4212 1,\
4213 1,\
4214 1,\
4215 1,\
4216 1,\
4217 1,\
4218 1,\
4219 2,\
4220 3,\
4221 4,\
4222 5,\
4223 6,\
4224 7,\
4225 8,\
4226 9,\
4227 10,\
4228 11,\
4229 12,\
4230 13,\
4231 14,\
4232 15,\
4233 16,\
4234 17,\
4235 18,\
4236 19,\
4237 20,\
4238 21,\
4239 22,\
4240 23,\
4241 24,\
4242 25,\
4243 },\
4244 doorsY = {},\
4245}",
4246 "--Editor (yaay make your own levels based on this \"engine\")\
4247--Again, written by me c:\
4248--(I know, I'm shit at programming but hey why not C:)\
4249\
4250--Variablen\
4251_ver = 0.1\
4252_verstr = \"0.1\"\
4253pfad = \"\"\
4254\
4255lvlData = {\
4256\009spawnPosX=\"1\",\
4257\009spawnPosY=\"1\",\
4258\009blocksX = {\
4259\
4260\009},\
4261\009blocksY = {\
4262\
4263\009}\
4264}\
4265\
4266--Funktionen\
4267function clear(bg, fg)\
4268\009term.setCursorPos(1,1)\
4269\009term.setBackgroundColor(bg)\
4270\009term.setTextColor(fg)\
4271\009term.clear()\
4272end\
4273\
4274function menu()\
4275\009clear(colors.black, colors.white)\
4276\009term.setBackgroundColor(colors.lime)\
4277\009term.setCursorPos(25, 6)\
4278\009term.write(\"NEW\")\
4279\009term.setCursorPos(24, 8)\
4280\009term.write(\"LOAD\")\
4281\009term.setCursorPos(24, 10)\
4282\009term.write(\"EXIT\")\
4283\009menu = true\
4284\009while menu do\
4285\009\009local event, button, x, y = os.pullEventRaw(\"mouse_click\")\
4286\009\009if button == 1 and x >= 25 and x <= 27 and y == 6 then\
4287\009\009\009menu = false\
4288\009\009\009menu2()\
4289\009\009end\
4290\009end\
4291end\
4292\
4293function menu2()\
4294\009clear(colors.black, colors.white)\
4295\009oldTerm = term.native()\
4296\009local graywindow = window.create(oldTerm, 15, 5, 20, 5)\
4297\009term.redirect(graywindow)\
4298\009term.setBackgroundColor(colors.lightGray)\
4299\009term.setCursorPos(1,1)\
4300\009term.clear()\
4301\009local textBox = window.create(term.current(), 2, 2, 18, 1)\
4302\009textBox.setBackgroundColor(colors.gray)\
4303\009textBox.setTextColor(colors.lime)\
4304\009textBox.clear()\
4305\009textBox.write(\"Enter Name/Desti..\")\
4306\009term.setBackgroundColor(colors.gray)\
4307\009term.setTextColor(colors.white)\
4308\009term.setCursorPos(2, 4)\
4309\009term.write(\"New\")\
4310\009term.setCursorPos(16, 4)\
4311\009term.write(\"Load\")\
4312\009menu = true\
4313\009while menu do\
4314\009\009local event, button, x, y = os.pullEvent(\"mouse_click\")\
4315\009\009if button == 1 and x >= 16 and x <= 34 and y == 6 then\
4316\009\009\009term.redirect(textBox)\
4317\009\009\009term.clear()\
4318\009\009\009term.setCursorPos(1,1)\
4319\009\009\009local eingabe = read()\
4320\009\009\009pfad = eingabe\
4321\009\009\009term.setCursorPos(1,1)\
4322\009\009\009term.clear()\
4323\009\009\009term.write(eingabe)\
4324\009\009\009term.redirect(graywindow)\
4325\009\009elseif button == 1 and x >= 16 and x <= 18 and y == 8 then\
4326\009\009\009if fs.exists(pfad..\".lvl\") then\
4327\009\009\009\009textBox.setCursorPos(1,1)\
4328\009\009\009\009textBox.clear()\
4329\009\009\009\009textBox.setTextColor(colors.red)\
4330\009\009\009\009textBox.write(\"Already exists.\")\
4331\009\009\009\009textBox.setTextColor(colors.lime)\
4332\009\009\009elseif pfad == \"\" or pfad == nil then\
4333\009\009\009\009textBox.setCursorPos(1,1)\
4334\009\009\009\009textBox.clear()\
4335\009\009\009\009textBox.setTextColor(colors.red)\
4336\009\009\009\009textBox.write(\"Please fill in.\")\
4337\009\009\009\009textBox.setTextColor(colors.lime)\
4338\009\009\009else\
4339\009\009\009\009term.redirect(oldTerm)\
4340\009\009\009\009term.setCursorPos(1,1)\
4341\009\009\009\009clear(colors.black, colors.white)\
4342\009\009\009\009menu = false\
4343\009\009\009\009print(\"Please draw your LevelDESIGN.\")\
4344\009\009\009\009print(\"This will be that, what you see when playing the level.\")\
4345\009\009\009\009sleep(3)\
4346\009\009\009\009shell.run(\"paint \"..pfad..\".lvl\")\
4347\009\009\009\009\
4348\009\009\009\009editor()\
4349\009\009\009end\
4350\009\009elseif button == 1 and x >= 30 and x <= 34 and y == 8 then\
4351\009\009\009if fs.exists(pfad..\".lvl\") and fs.exists(pfad..\".lvlDat\") then\
4352\009\009\009\009term.redirect(oldTerm)\
4353\009\009\009\009menu = false\
4354\009\009\009\009editor(\"true\")\
4355\009\009\009elseif fs.exists(pfad..\".lvl\") then\
4356\009\009\009\009term.redirect(oldTerm)\
4357\009\009\009\009menu = false\
4358\009\009\009\009editor(\"false\")\
4359\009\009\009else\
4360\009\009\009\009textBox.setCursorPos(1,1)\
4361\009\009\009\009textBox.clear()\
4362\009\009\009\009textBox.setTextColor(colors.red)\
4363\009\009\009\009textBox.write(\"Doesn't exist.\")\
4364\009\009\009\009textBox.setTextColor(colors.lime)\
4365\009\009\009end\
4366\009\009end\
4367\009end\
4368\
4369end\
4370\
4371function loadDat()\
4372\009local file = fs.open(pfad..\".lvlDat\",\"r\")\
4373\009local inhalt = file.readAll()\
4374\009lvlData = textutils.unserialize(inhalt)\
4375\009file.close()\
4376end\
4377\
4378function editor(loadData)\
4379\009clear(colors.black, colors.white)\
4380\009lvl = paintutils.loadImage(pfad..\".lvl\")\
4381\009paintutils.drawImage(lvl, 1, 1)\
4382\009if loadData == \"true\" then\
4383\009\009loadDat()\
4384\009\009reloadMap()\
4385\009end\
4386\009term.setCursorPos(1,19)\
4387\009term.setBackgroundColor(colors.lightGray)\
4388\009term.clearLine()\
4389\009term.setBackgroundColor(colors.lime)\
4390\009term.write(\" Block \")\
4391\009term.setBackgroundColor(colors.lightGray)\
4392\009term.write(\" \")\
4393\009term.setBackgroundColor(colors.lime)\
4394\009term.write(\" Spawn \")\
4395\009term.setBackgroundColor(colors.lightGray)\
4396\009term.write(\" \")\
4397\009term.setBackgroundColor(colors.lime)\
4398\009term.write(\" Remove \")\
4399\009editing = true\
4400\009while editing do\
4401\009\009local event, button, x, y = os.pullEventRaw()\
4402\009\009if event == \"mouse_click\" and button == 1 and x >= 1 and x <= 7 and y == 19 then\
4403\009\009\009currentBlock = \"block\"\
4404\009\009elseif event == \"mouse_click\" and button == 1 and x >= 9 and x <= 15 and y == 19 then\
4405\009\009\009currentBlock = \"spawn\"\
4406\009\009elseif event == \"mouse_click\" and button == 1 and x >= 17 and x <= 24 and y == 19 then\
4407\009\009\009currentBlock = \"remove\"\
4408\009\009elseif event == \"mouse_click\" and button == 1 and x >= 1 and x <= 51 and y >= 1 and y <= 18 then\
4409\009\009\009if currentBlock == \"block\" then\
4410\009\009\009\009term.setCursorPos(x, y)\
4411\009\009\009\009term.setBackgroundColor(colors.brown)\
4412\009\009\009\009term.setTextColor(colors.white)\
4413\009\009\009\009term.write(\"B\")\
4414\009\009\009\009local x = tostring(x)\
4415\009\009\009\009local y = tostring(y)\
4416\009\009\009\009table.insert(lvlData.blocksX, x)\
4417\009\009\009\009table.insert(lvlData.blocksY, y)\
4418\009\009\009\009reloadMap()\
4419\009\009\009elseif currentBlock == \"spawn\" then\
4420\009\009\009\009term.setCursorPos(x, y)\
4421\009\009\009\009term.setBackgroundColor(colors.blue)\
4422\009\009\009\009term.setTextColor(colors.white)\
4423\009\009\009\009term.write(\"S\")\
4424\009\009\009\009local x = tostring(x)\
4425\009\009\009\009local y = tostring(y)\
4426\009\009\009\009lvlData.spawnPosX = x\
4427\009\009\009\009lvlData.spawnPosY = y\
4428\009\009\009\009reloadMap()\
4429\009\009\009elseif currentBlock == \"remove\" then\
4430\009\009\009\009for _, block in ipairs(lvlData.blocksX) do\
4431\009\009\009\009\009local blockY = tonumber(lvlData.blocksY[_])\
4432\009\009\009\009\009local block = tonumber(block)\
4433\009\009\009\009\009if x == block and y == blockY then\
4434\009\009\009\009\009\009lvlData.blocksX[_] = nil\
4435\009\009\009\009\009\009lvlData.blocksY[_] = nil\
4436\009\009\009\009\009end\
4437\009\009\009\009end\
4438\009\009\009\009if x == lvlData.spawnPosX and y == lvlData.spawnPosY then\
4439\009\009\009\009\009lvlData.spawnPosX = \"1\"\
4440\009\009\009\009\009lvlData.spawnPosY = \"1\"\
4441\009\009\009\009end\
4442\009\009\009\009reloadMap()\
4443\009\009\009end\
4444\009\009elseif event == \"key\" and button == keys.s then\
4445\009\009\009local file = fs.open(pfad..\".lvlDat\",\"w\")\
4446\009\009\009local lvlData = textutils.serialize(lvlData)\
4447\009\009\009file.write(lvlData)\
4448\009\009\009file.close()\
4449\009\009\009clear(colors.black, colors.white)\
4450\
4451\009\009\009editing = false\
4452\009\009\009break\
4453\009\009end\
4454\
4455\009end\
4456end\
4457\
4458function reloadMap()\
4459\009clear(colors.black, colors.white)\
4460\009term.setCursorPos(1,19)\
4461\009term.setBackgroundColor(colors.lightGray)\
4462\009term.clearLine()\
4463\009term.setBackgroundColor(colors.lime)\
4464\009term.write(\" Block \")\
4465\009term.setBackgroundColor(colors.lightGray)\
4466\009term.write(\" \")\
4467\009term.setBackgroundColor(colors.lime)\
4468\009term.write(\" Spawn \")\
4469\009term.setBackgroundColor(colors.lightGray)\
4470\009term.write(\" \")\
4471\009term.setBackgroundColor(colors.lime)\
4472\009term.write(\" Remove \")\
4473\009term.setCursorPos(1,1)\
4474\009paintutils.drawImage(lvl, 1, 1)\
4475\009for _, block in ipairs(lvlData.blocksX) do\
4476\009\009local blockY = tonumber(lvlData.blocksY[_])\
4477\009\009local block = tonumber(block)\
4478\009\009term.setCursorPos(block, blockY)\
4479\009\009term.setBackgroundColor(colors.brown)\
4480\009\009term.setTextColor(colors.white)\
4481\009\009term.write(\"B\")\
4482\009end\
4483\009local sX = tonumber(lvlData.spawnPosX)\
4484\009local sY = tonumber(lvlData.spawnPosY)\
4485\009term.setCursorPos(sX, sY)\
4486\009term.setBackgroundColor(colors.blue)\
4487\009term.setTextColor(colors.white)\
4488\009term.write(\"S\")\
4489end\
4490\
4491\
4492\
4493--Code\
4494menu2()",
4495 "{\
4496 world = \"house2\",\
4497 inventory = {},\
4498 y = 2,\
4499 x = 13,\
4500}",
4501 "{\
4502 world = \"house1\",\
4503 inventory = {},\
4504 y = 2,\
4505 x = 2,\
4506}",
4507 "0000000000000000000000000000000000000000 3db53ac57b15711f1f6a4054203a234078587bf0 Piorjade <piorjadelp@gmail.com> 1466956418 +0200\009commit (initial): :boom::camel: Added .gitattributes & .gitignore files\
45083db53ac57b15711f1f6a4054203a234078587bf0 1f40b39d63a394d16ce02d44fc648ca84548789f Piorjade <piorjadelp@gmail.com> 1466956450 +0200\009commit: Added Code to GitHub\
45091f40b39d63a394d16ce02d44fc648ca84548789f 107081fe84aa72683e0a970bfad56ca79c1ed2e7 Piorjade <piorjadelp@gmail.com> 1466956702 +0200\009commit: Renamed to startup",
4510 "107081fe84aa72683e0a970bfad56ca79c1ed2e7",
4511 "0000000000000000000000000000000000000000 1f40b39d63a394d16ce02d44fc648ca84548789f Piorjade <piorjadelp@gmail.com> 1466956493 +0200\009update by push\
45121f40b39d63a394d16ce02d44fc648ca84548789f 107081fe84aa72683e0a970bfad56ca79c1ed2e7 Piorjade <piorjadelp@gmail.com> 1466956711 +0200\009update by push",
4513}
4514folders = {
4515 ".git",
4516 "server",
4517 ".git/hooks",
4518 ".git/info",
4519 ".git/logs",
4520 ".git/objects",
4521 ".git/refs",
4522 "server/maps",
4523 "server/saves",
4524 ".git/logs/refs",
4525 ".git/objects/08",
4526 ".git/objects/10",
4527 ".git/objects/1f",
4528 ".git/objects/24",
4529 ".git/objects/27",
4530 ".git/objects/2a",
4531 ".git/objects/3d",
4532 ".git/objects/3f",
4533 ".git/objects/45",
4534 ".git/objects/4b",
4535 ".git/objects/4e",
4536 ".git/objects/54",
4537 ".git/objects/5c",
4538 ".git/objects/61",
4539 ".git/objects/70",
4540 ".git/objects/7b",
4541 ".git/objects/82",
4542 ".git/objects/83",
4543 ".git/objects/95",
4544 ".git/objects/9c",
4545 ".git/objects/b0",
4546 ".git/objects/bd",
4547 ".git/objects/c0",
4548 ".git/objects/cd",
4549 ".git/objects/cf",
4550 ".git/objects/e6",
4551 ".git/objects/info",
4552 ".git/objects/pack",
4553 ".git/refs/heads",
4554 ".git/refs/remotes",
4555 ".git/refs/tags",
4556 "server/maps/house1",
4557 "server/maps/house2",
4558 "server/saves/Player",
4559 "server/saves/test",
4560 ".git/logs/refs/heads",
4561 ".git/logs/refs/remotes",
4562 ".git/refs/remotes/origin",
4563 ".git/logs/refs/remotes/origin",
4564}
4565local args = {...}
4566if #args < 1 then
4567 print('Usage: <file> <destination>')
4568else
4569 args[1] = args[1]..'/' for _, folder in ipairs(folders) do
4570 fs.makeDir(args[1]..folder)
4571 end
4572 for _, file in ipairs(files) do
4573 local file = fs.open(args[1]..file, 'w')
4574 file.write(fileData[_])
4575 file.close()
4576 end
4577end