· 8 years ago · Jan 27, 2018, 02:00 AM
1--#### Wojbie's API 3.0 - util module
2
3--Require table to return
4local util = shell and {} or (_ENV or getfenv())
5
6--### Utility functions module - ".w/util.lua"
7
8--# [[Adaptation of the Secure Hashing Algorithm (SHA-244/256) Found Here: http://lua-users.org/wiki/SecureHashAlgorithm Using an adapted version of the bit library Found Here: https://bitbucket.org/Boolsheet/bslf/src/1ee664885805/bit.lua Taken from http://www.computercraft.info/forums2/index.php?/topic/8169-sha-256-in-pure-lua/ and http://pastebin.com/gsFrNjbt]]
9local hash = function() error("Hash function did not load correctly") end do
10local MOD = 2^32 local MODM = MOD-1 local function memoize(f) local mt = {} local t = setmetatable({}, mt) function mt:__index(k) local v = f(k) t[k] = v return v end return t end local function make_bitop_uncached(t, m) local function bitop(a, b) local res,p = 0,1 while a ~= 0 and b ~= 0 do local am, bm = a % m, b % m res = res + t[am][bm] * p a = (a - am) / m b = (b - bm) / m p = p*m end res = res + (a + b) * p return res end return bitop end local function make_bitop(t) local op1 = make_bitop_uncached(t,2^1) local op2 = memoize(function(a) return memoize(function(b) return op1(a, b) end) end) return make_bitop_uncached(op2, 2 ^ (t.n or 1)) end local bxor1 = make_bitop({[0] = {[0] = 0,[1] = 1}, [1] = {[0] = 1, [1] = 0}, n = 4}) local function bxor(a, b, c, ...) local z = nil if b then a = a % MOD b = b % MOD z = bxor1(a, b) if c then z = bxor(z, c, ...) end return z elseif a then return a % MOD else return 0 end end local function band(a, b, c, ...) local z if b then a = a % MOD b = b % MOD z = ((a + b) - bxor1(a,b)) / 2 if c then z = bit32_band(z, c, ...) end return z elseif a then return a % MOD else return MODM end end local function bnot(x) return (-1 - x) % MOD end local function rshift1(a, disp) if disp < 0 then return lshift(a,-disp) end return math.floor(a % 2 ^ 32 / 2 ^ disp) end local function rshift(x, disp) if disp > 31 or disp < -31 then return 0 end return rshift1(x % MOD, disp) end local function lshift(a, disp) if disp < 0 then return rshift(a,-disp) end return (a * 2 ^ disp) % 2 ^ 32 end local function rrotate(x, disp) x = x % MOD disp = disp % 32 local low = band(x, 2 ^ disp - 1) return rshift(x, disp) + lshift(low, 32 - disp) end local k = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, } local function str2hexa(s) return (string.gsub(s, ".", function(c) return string.format("%02x", string.byte(c)) end)) end local function num2s(l, n) local s = "" for i = 1, n do local rem = l % 256 s = string.char(rem) .. s l = (l - rem) / 256 end return s end local function s232num(s, i) local n = 0 for i = i, i + 3 do n = n*256 + string.byte(s, i) end return n end local function preproc(msg, len) local extra = 64 - ((len + 9) % 64) len = num2s(8 * len, 8) msg = msg .. "\128" .. string.rep("\0", extra) .. len assert(#msg % 64 == 0) return msg end local function initH256(H) H[1] = 0x6a09e667 H[2] = 0xbb67ae85 H[3] = 0x3c6ef372 H[4] = 0xa54ff53a H[5] = 0x510e527f H[6] = 0x9b05688c H[7] = 0x1f83d9ab H[8] = 0x5be0cd19 return H end local function digestblock(msg, i, H) local w = {} for j = 1, 16 do w[j] = s232num(msg, i + (j - 1)*4) end for j = 17, 64 do local v = w[j - 15] local s0 = bxor(rrotate(v, 7), rrotate(v, 18), rshift(v, 3)) v = w[j - 2] w[j] = w[j - 16] + s0 + w[j - 7] + bxor(rrotate(v, 17), rrotate(v, 19), rshift(v, 10)) end local a, b, c, d, e, f, g, h = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] for i = 1, 64 do local s0 = bxor(rrotate(a, 2), rrotate(a, 13), rrotate(a, 22)) local maj = bxor(band(a, b), band(a, c), band(b, c)) local t2 = s0 + maj local s1 = bxor(rrotate(e, 6), rrotate(e, 11), rrotate(e, 25)) local ch = bxor (band(e, f), band(bnot(e), g)) local t1 = h + s1 + ch + k[i] + w[i] h, g, f, e, d, c, b, a = g, f, e, d + t1, c, b, a, t1 + t2 end H[1] = band(H[1] + a) H[2] = band(H[2] + b) H[3] = band(H[3] + c) H[4] = band(H[4] + d) H[5] = band(H[5] + e) H[6] = band(H[6] + f) H[7] = band(H[7] + g) H[8] = band(H[8] + h) end local function sha256(msg) msg = preproc(msg, #msg) local H = initH256({}) for i = 1, #msg, 64 do digestblock(msg, i, H) end return str2hexa(num2s(H[1], 4) .. num2s(H[2], 4) .. num2s(H[3], 4) .. num2s(H[4], 4) .. num2s(H[5], 4) .. num2s(H[6], 4) .. num2s(H[7], 4) .. num2s(H[8], 4)) end hash = sha256 end util["hash"] = hash
11
12--# Default Colors list
13
14local tHex = {
15 [ colors.white ] = "0",
16 [ colors.orange ] = "1",
17 [ colors.magenta ] = "2",
18 [ colors.lightBlue ] = "3",
19 [ colors.yellow ] = "4",
20 [ colors.lime ] = "5",
21 [ colors.pink ] = "6",
22 [ colors.gray ] = "7",
23 [ colors.lightGray ] = "8",
24 [ colors.cyan ] = "9",
25 [ colors.purple ] = "a",
26 [ colors.blue ] = "b",
27 [ colors.brown ] = "c",
28 [ colors.green ] = "d",
29 [ colors.red ] = "e",
30 [ colors.black ] = "f",
31} util.tHex = tHex
32
33local tPaint = { --minecraft:dye
34 [ "0" ] = 15,
35 [ "1" ] = 14,
36 [ "2" ] = 13,
37 [ "3" ] = 12,
38 [ "4" ] = 11,
39 [ "5" ] = 10,
40 [ "6" ] = 9,
41 [ "7" ] = 8,
42 [ "8" ] = 7,
43 [ "9" ] = 6,
44 [ "a" ] = 5,
45 [ "b" ] = 4,
46 [ "c" ] = 3,
47 [ "d" ] = 2,
48 [ "e" ] = 1,
49 [ "f" ] = 0,
50} util.tPaint = tPaint
51
52local tRGB = {
53 [ "0" ] = 0xF0F0F0,
54 [ "1" ] = 0xF2B233,
55 [ "2" ] = 0xE57FD8,
56 [ "3" ] = 0x99B2F2,
57 [ "4" ] = 0xDEDE6C,
58 [ "5" ] = 0x7FCC19,
59 [ "6" ] = 0xF2B2CC,
60 [ "7" ] = 0x4C4C4C,
61 [ "8" ] = 0x999999,
62 [ "9" ] = 0x4C99B2,
63 [ "a" ] = 0xB266E5,
64 [ "b" ] = 0x3366CC,
65 [ "c" ] = 0x7F664C,
66 [ "d" ] = 0x57A64E,
67 [ "e" ] = 0xCC4C4C,
68 [ "f" ] = 0x111111,
69} util.tRGB = tRGB
70
71local tDefaultPallette = {
72 [ colors.white ] = 0xF0F0F0,
73 [ colors.orange ] = 0xF2B233,
74 [ colors.magenta ] = 0xE57FD8,
75 [ colors.lightBlue ] = 0x99B2F2,
76 [ colors.yellow ] = 0xDEDE6C,
77 [ colors.lime ] = 0x7FCC19,
78 [ colors.pink ] = 0xF2B2CC,
79 [ colors.gray ] = 0x4C4C4C,
80 [ colors.lightGray ] = 0x999999,
81 [ colors.cyan ] = 0x4C99B2,
82 [ colors.purple ] = 0xB266E5,
83 [ colors.blue ] = 0x3366CC,
84 [ colors.brown ] = 0x7F664C,
85 [ colors.green ] = 0x57A64E,
86 [ colors.red ] = 0xCC4C4C,
87 [ colors.black ] = 0x111111,
88} util.tDefaultPallette = tDefaultPallette
89
90local tVibrantPallette = {
91 [ colors.white ] = 0xFFFFFF,
92 [ colors.orange ] = 0xFF6300,
93 [ colors.magenta ] = 0xFF00DE,
94 [ colors.lightBlue ] = 0x00C3FF,
95 [ colors.yellow ] = 0xFFFF00,
96 [ colors.lime ] = 0x91FF00,
97 [ colors.pink ] = 0xFF6DA8,
98 [ colors.gray ] = 0x383737,
99 [ colors.lightGray ] = 0xA9A9A9,
100 [ colors.cyan ] = 0x00FFFF,
101 [ colors.purple ] = 0x7700FF,
102 [ colors.blue ] = 0x0000FF,
103 [ colors.brown ] = 0x4C2700,
104 [ colors.green ] = 0x00FF00,
105 [ colors.red ] = 0xFF0000,
106 [ colors.black ] = 0x000000,
107} util.tVibrantPallette = tVibrantPallette
108
109--# Palette reseter
110local function resetPalette(parent,bool)
111 if not parent then parent = term end
112 if type(parent) ~= "table" or not parent.setPaletteColour then error("Malformed terminal object",2) end
113 for i=0,15 do
114 local c = 2 ^ i
115 parent.setPaletteColour(c, colors.rgb8( bool and tVibrantPallette[ c ] or tDefaultPallette[ c ] ) )
116 end
117end util["resetPalette"] = resetPalette
118
119--# Asks yes/no type of question.
120local function yn(A)
121 if not A then return false end
122 local key
123 write(A.."[y/n]:")
124 while true do
125 _,key = os.pullEvent("char")
126 if key=="y" then write(key.."\n") return true
127 elseif key=="n" then write(key.."\n") return false
128 end
129 end
130end util["yn"] = yn
131
132local g_tLuaKeywords = {
133 [ "and" ] = true,
134 [ "break" ] = true,
135 [ "do" ] = true,
136 [ "else" ] = true,
137 [ "elseif" ] = true,
138 [ "end" ] = true,
139 [ "false" ] = true,
140 [ "for" ] = true,
141 [ "function" ] = true,
142 [ "if" ] = true,
143 [ "in" ] = true,
144 [ "local" ] = true,
145 [ "nil" ] = true,
146 [ "not" ] = true,
147 [ "or" ] = true,
148 [ "repeat" ] = true,
149 [ "return" ] = true,
150 [ "then" ] = true,
151 [ "true" ] = true,
152 [ "until" ] = true,
153 [ "while" ] = true,
154}
155
156local function serializeImplFlat( t, tTracking )
157 local sType = type(t)
158 if sType == "table" then
159 if tTracking[t] ~= nil then
160 error( "Cannot serialize table with recursive entries", 0 )
161 end
162 tTracking[t] = true
163
164 if next(t) == nil then
165 -- Empty tables are simple
166 return "{}"
167 else
168 -- Other tables take more work
169 local sResult = "{"
170 local tSeen = {}
171 for k,v in ipairs(t) do
172 tSeen[k] = true
173 local sEntry = serializeImplFlat( v, tTracking )
174 if sEntry then
175 sResult = sResult .. sEntry .. ","
176 end
177 end
178 for k,v in pairs(t) do
179 if not tSeen[k] then
180 if type(k) == "string" and not g_tLuaKeywords[k] and string.match( k, "^[%a_][%a%d_]*$" ) then
181 local sEntry = serializeImplFlat( v, tTracking )
182 if sEntry then
183 sResult = sResult .. k .. "=" .. sEntry .. ","
184 end
185 else
186 local sEntry1,sEntry2 = serializeImplFlat( k, tTracking ),serializeImplFlat( v, tTracking )
187 if sEntry1 and sEntry2 then
188 sResult = sResult .. "[" .. sEntry1 .. "]=" .. sEntry2 .. ","
189 end
190 end
191 end
192 end
193 if string.sub(sResult, -1) == "," then --remove last , Saving world one char at time.
194 sResult = string.sub(sResult, 1, -2)
195 end
196 return sResult .. "}"
197 end
198
199 elseif sType == "string" then
200 return string.format( "%q", t )
201
202 elseif sType == "number" or sType == "boolean" or sType == "nil" then
203 return tostring(t)
204
205 else
206 --This way if tTracking is empty that means unserializable was passed into the function. If not then something inside table is unserializabe and will be skipped.
207 if not next(tTracking) then
208 error( "Cannot serialize type "..sType, 0 )
209 end
210 end
211end
212
213local function serializeFlat( t )
214 local tTracking = {}
215 return serializeImplFlat( t, tTracking )
216end util["serializeFlat"] = serializeFlat
217
218--## Make it flatten output.
219--# Serializes recursive tables
220local function serializeRec(t)
221 if type(t) ~= "table" then
222 return serializeFlat(t) --it was not table - do normal serialize
223 end
224 tImput={t}
225 tList={}
226 tLook={}
227 tMark={}
228 tOutp={"(function() \n"}
229 local cur,nTab
230 while #tImput>0 do
231 cur = table.remove(tImput)
232 nTab=#tList+1
233 tList[nTab]=cur
234 nTab="t"..nTab
235 tLook[cur] = nTab
236 if next(cur) == nil then
237 -- Empty tables are simple
238 tOutp[#tOutp+1]=nTab.."={} \n"
239 else
240 --Look over the table, make copy
241 local Basic={}
242 for k,v in pairs(cur) do
243 if type(k) =="table" or type(v) =="table" then
244 table.insert(tMark,{cur,k,v})
245 if type(k) =="table" and not tLook[k] then tImput[#tImput+1]=k end
246 if type(v) =="table" and not tLook[v] then tImput[#tImput+1]=v end
247 else
248 Basic[k]=v
249 end
250 end
251 tOutp[#tOutp+1]=nTab.."="..serializeFlat(Basic).." \n"
252 end
253 end
254 while #tMark>0 do
255 cur = table.remove(tMark)
256 tOutp[#tOutp+1]=tLook[cur[1]].."["..(type(cur[2]) =="table" and tLook[cur[2]] or serializeFlat(cur[2])).."]="..(type(cur[3]) =="table" and tLook[cur[3]] or serializeFlat(cur[3])).."\n"
257 end
258 tOutp[#tOutp+1]="return t1 end)()"
259 return table.concat(tOutp)
260end util["serializeRec"] = serializeRec
261
262--# Copy of completeMultipleChoice i use in my programs.
263local function completeMultipleChoice( sText, tOptions, bAddSpaces, tOptionsGhosts )
264 local tResults = {}
265 local tGhosts = {}
266 for n=1,#tOptions do
267 local sOption = tOptions[n]
268 if #sOption + (bAddSpaces and 1 or 0) > #sText and string.sub( sOption, 1, #sText ) == sText then
269 local sResult = string.sub( sOption, #sText + 1 )
270 if bAddSpaces then
271 table.insert( tResults, sResult .. " " )
272 else
273 table.insert( tResults, sResult )
274 end
275 if tOptionsGhosts then
276 if bAddSpaces then
277 table.insert( tGhosts, tOptionsGhosts[n] or "")
278 else
279 table.insert( tGhosts, (tOptionsGhosts[n] and " "..tOptionsGhosts[n]) or "")
280 end
281
282 end
283 end
284 end
285 return tResults, tOptionsGhosts and tGhosts
286end util["completeMultipleChoice"] = completeMultipleChoice
287
288--# Number clamp
289local function clamp(nMin,nNumber,nMax)
290 if type(nMin) ~= "number" or type(nNumber) ~= "number" or type(nMax) ~= "number" then error("Not a number.",2) end
291 return math.min(math.max( nMin, nNumber ), nMax )
292end util["clamp"] = clamp
293
294--# Copy table - if more than one merge them - in case of overwriting values last one gets its way
295local function copyTable(...)
296tArgs={...}
297local B={}
298 for _,A in pairs(tArgs) do
299 if A and type(A)=="table" then
300 for i,k in pairs(A) do
301 if type(k)=="table" then B[i]=copyTable( B[i] or {},k)
302 else B[i]=k end
303 end
304 end
305 end
306return B
307end util["copyTable"] = copyTable
308
309--# Clone table - duplicates single table preserving all recursive setups.
310local function cloneTable(source)
311 local lookup = {}
312 local output = {}
313 lookup[source] = output
314
315 local todo = {}
316 table.insert(todo,{source,output})
317
318 while #todo > 0 do
319 local job = table.remove(todo,1)
320 local out = job[2]
321 for i,k in pairs(job[1]) do
322 if type(k)=="table" then
323 if not lookup[k] then lookup[k] = {} table.insert(todo,{k,lookup[k]}) end
324 out[i]=lookup[k]
325 else out[i]=k end
326 end
327 end
328
329 return output
330end util["cloneTable"] = cloneTable
331
332--# Write on Center - writes proveided text centered horizontally or vertically on term object (defaults to term)
333local function writeOnCenter(tTerminal,tData,nX,nY)
334 if tTerminal and not tData then
335 tData = tTerminal
336 tTerminal = nil
337 end
338 tTerminal = tTerminal or term
339 if type(tData) == "string" then tData={{tData}} end
340 local oX,oY = tTerminal.getSize()
341 local cX,cY = #tData[1][1],#tData[1]
342 nX = nX or math.floor((oX-cX)/2)+1
343 nY = nY or math.floor((oY-cY)/2)+1
344
345 for i=1,cY do
346 if i > 1 and nY+i-1 > oY then term.scroll(1) nY = nY-1 end
347 tTerminal.setCursorPos(nX,nY+i-1)
348 tTerminal.blit(tData[1][i],tData[2] and tData[2][i] or string.rep(tHex[tTerminal.getTextColor()],#tData[1][i]) ,tData[3] and tData[3][i] or string.rep(tHex[tTerminal.getBackgroundColor()],#tData[1][i]))
349 end
350end util["writeOnCenter"] = writeOnCenter
351
352--# Spacial table that will transferr all functions call to each and every sub table.
353local function createMultitable(fluid,...)
354
355 local output = {}
356 local tab = {...}
357 local fluid = fluid
358 if #tab==1 and tab[1] and type(tab[1])=="table" then tab = tab[1] end
359 if #tab==0 then error("Expected bool and table of tables or any tables to table. I know it makes no sense.", 2) end
360
361 local function makeWrap(tab,key)
362 return function(...)
363 local ret={}
364 local tArgs=table.pack(...)
365 for i,k in ipairs(tab) do
366 if k[key] then
367 if #ret==0 then ret=table.pack(k[key](table.unpack(tArgs))) --ret contains returns from first table that returned anything.
368 else k[key](table.unpack(tArgs)) end
369 end
370 end
371 return table.unpack(ret)
372 end
373 end
374
375 local function repopulate()
376 for key,fun in pairs(tab[1]) do --create static table of multitable functions using first one as template
377 rawset(output,key,makeWrap(tab,key))
378 end
379 end
380 local function clean()
381 for key,fun in pairs(output) do --remove all parts of static table that stopped existing.
382 if not tab[1][key] then rawset(output,key,nil) end
383 end
384 end
385
386 local manymeta={ --Anytime index is requested fist table is used as refference.
387 ["__index"]=function (parent , key)
388 if tab and tab[1] and tab[1][key] then --If it has value it tested then
389 if type(tab[1][key]) =="function" then --If its function then a function that calls all tables in row is made
390 local wrap = makeWrap(tab,key)
391 if not fluid then rawset(output,key,wrap) end --If for some reason called function don't exists add it to static table
392 return wrap
393 else
394 return tab[1][key] --If its not a function then its just given out.
395 end
396 else
397 return nil --Of it not exist in first table give nothing
398 end
399 end,
400 ["__newindex"]=function (parent, key, value) --If someone wants to add anything to the table
401 --do nothing.
402 end,
403 ["__call"]=function (parent, key) --If someone calls table like function give him direct acces to table list.
404 if key then tab = key if not fluid then clean() repopulate() end end --Allows swapping source table. WARNING If tab is changed using different method repopulate will fail. NO SANITY CHECKS!!
405 return tab
406 end,
407 ["__len"]=function (parent, key) --Not sure if it works but this is giving the leanght of first table or 0 if there is no first table.
408 return (tab[1] and #tab[1]) or 0
409 end,
410 ["__metatable"]=false,--No touching the metatable.
411 --["__type"]="WojbieManyMeta",--Custom type? Not in current version and not sure if wise. Commented out for now.
412 }
413
414 if not fluid then repopulate() end
415
416 return setmetatable(output,manymeta) --create acctual manymeta table and return it
417
418end util["createMultitable"] = createMultitable
419
420--Basic TLCO implementation.
421local function tlco(standard,...)
422
423 local tExtraFunctions = table.pack(...)
424
425 if standard then
426 table.insert(tExtraFunctions,1,function() os.run( {},
427 (type(standard) == "string" and "/rom/programs/shell.lua")
428 or (term.isColour() and settings.get( "bios.use_multishell" ) and "/rom/programs/advanced/multishell.lua" )
429 or "/rom/programs/shell.lua",
430 (type(standard) == "string" and standard) or nil
431 ) os.run( {}, "rom/programs/shutdown.lua" ) end)
432 table.insert(tExtraFunctions,2,function() os.loadAPI( "/rom/apis/rednet.lua" ) rednet.run() end)
433 end
434
435 local function main()
436
437 _G._AfTeRtLcO = true
438 term.redirect( term.native() )
439 local ok, err = pcall( function()
440 parallel.waitForAny(table.unpack(tExtraFunctions))
441 end )
442
443 -- If the shell errored, let the user read it.
444 term.redirect( term.native() )
445 if not ok then
446 printError( err )
447 pcall( function()
448 term.setCursorBlink( false )
449 print( "Press any key to continue" )
450 os.pullEvent( "key" )
451 end )
452 end
453
454 -- End
455 os.shutdown()
456
457 end
458
459 --TLCO HERE
460 local shutdown = _G.os.shutdown
461 function _G.os.shutdown()
462 function _G.os.shutdown()
463 _G.os.shutdown = shutdown
464 main()
465 end
466 end
467
468 os.queueEvent("terminate")
469
470end util["TLCO"] = TLCO
471
472--Basic Menu implementation (List mode) (tFunctions is optional and will run function assigned to number selected from tOptions is exists)
473local function simpleMenu(tOptions,nStartPoint,tFunctions,nTimeout)
474
475 if type( tOptions ) ~= "table" or
476 (nStartPoint ~= nil and type( nStartPoint ) ~= "number") or
477 (tFunctions ~= nil and type( tFunctions ) ~= "table") then
478 error( "Expected table, [number], [table]", 2 )
479 end
480 tFunctions = tFunctions or {}
481
482 local x,y=term.getSize()
483 local cText,CBack = term.getTextColor(),term.getBackgroundColor()
484 local selected = clamp(1,nStartPoint or 1,#tOptions)
485 local offset = 0
486
487 local function list()
488 offset = #tOptions <= y and 0 or clamp(0,selected-math.floor(y/2),#tOptions-y)
489 term.clear()
490 for i=1,#tOptions do
491 term.setCursorPos(1,i)
492 if i+offset == selected then term.setTextColor(CBack) term.setBackgroundColor(cText) term.clearLine()
493 elseif i+offset == selected+1 then term.setTextColor(cText) term.setBackgroundColor(CBack) end
494 term.write(tOptions[i+offset])
495 end
496 term.setTextColor(cText) term.setBackgroundColor(CBack)
497 end
498 list()
499 local event,x,y,timer
500 if nTimeout then timer=os.startTimer(nTimeout) end
501 while true do
502 event= {os.pullEvent()}
503 x,y = term.getSize()
504 if event[1]=="key" then
505 if event[2]==keys.numPadEnter or event[2]==keys.enter then
506 if type(tFunctions) == "function" then -- If table of functions is a function then run it with seleced number
507 return tFunctions(selected)
508 elseif tFunctions[selected] then --If not function (like true) its non selectable entry. If a function run it.
509 if type(tFunctions[selected]) == "function" then return selected,tFunctions[selected]() end
510 else
511 return selected
512 end
513 elseif event[2]==keys.down then
514 selected=math.min(selected+1,#tOptions)
515 list()
516 elseif event[2]==keys.up then
517 selected=math.max(selected-1,1)
518 list()
519 elseif event[2]==keys.pageDown then
520 selected=math.min(selected+y,#tOptions)
521 list()
522 elseif event[2]==keys.pageUp then
523 selected=math.max(selected-y,1)
524 list()
525 end
526 elseif event[1] == "mouse_click" then
527 local line = offset + event[4]
528 if line == selected then
529 if type(tFunctions) == "function" then -- If table of functions is a function then run it with seleced number
530 return tFunctions(selected)
531 elseif tFunctions[selected] then --If not function (like true) its non selectable entry. If a function run it.
532 if type(tFunctions[selected]) == "function" then return selected,tFunctions[selected]() end
533 else
534 return selected
535 end
536 else
537 selected = clamp(1,line,#tOptions)
538 list()
539 end
540 elseif event[1] == "mouse_scroll" then
541 selected = clamp(1,selected+event[2],#tOptions)
542 list()
543 elseif event[1] == "timer" and event[2]==timer then
544 return -1
545 end
546 end
547
548end util["simpleMenu"] = simpleMenu
549
550--Basic motivator
551local tQuotes={
552[[1. Pillage, then burn.]],[[2. A Sergeant in motion outranks a Lieutenant who doesn't know what's going on .]],[[3. An ordnance technician at a dead run outranks everybody .]],[[4. Close air support covereth a multitude of sins.]],[[5. Close air support and friendly fire should be easier to tell apart.]],[[6. If violence wasn't your last resort, you failed to resort to enough of it.]],[[7. If the food is good enough the grunts will stop complaining about the incoming fire.]],[[8. Mockery and derision have their place. Usually, it's on the far side of the airlock.]],[[9. Never turn your back on an enemy.]],[[10. Sometimes the only way out is through... through the hull.]],[[11. Everything is air-droppable at least once.]],[[12. A soft answer turneth away wrath. Once wrath is looking the other way, shoot it in the head.]],[[13. Do unto others.]],[[14. "Mad Science" means never stopping to ask "what's the worst thing that could happen?".]],[[15. Only you can prevent friendly fire.]],[[16. Your name is in the mouth of others: be sure it has teeth.]],[[17. The longer everything goes according to plan, the bigger the impending disaster.]],[[18. If the officers are leading from in front, watch for an attack from the rear.]],[[19. The world is richer when you turn enemies into friends, but that's not the same as you being richer.]],[[20. If you're not willing to shell your own position, you're not willing to win.]],[[21. Give a man a fish, feed him for a day. Take his fish away and tell him he's lucky just to be alive, and he'll figure out how to catch another one for you to take tomorrow.]],[[22. If you can see the whites of their eyes, somebody's done something wrong.]],[[23. The company mess and friendly fire should be easier to tell apart.]],[[24. Any sufficiently advanced technology is indistinguishable from a big gun.]],[[25. If a manufacturer's warranty covers the damage you do, you didn't do enough damage.]],[[26. "Fire and Forget" is fine, provided you never actually forget.]],[[27. Don't be afraid to be the first to resort to violence.]],[[28. If the price of collateral damage is high enough, you might be able to get paid to bring ammunition home with you.]],[[29. The enemy of my enemy is my enemy's enemy, no more, no less.]],[[30. A little trust goes a long way. The less you use, the further you'll go.]],[[31. Only cheaters prosper.]],[[32. Anything is amphibious if you can get it back out of the water.]],[[33. If you're leaving tracks, you're being followed]],[[34. If you're leaving scorch-marks, you need a bigger gun.]],[[35. That which does not kill me has made a tactical error.]],[[36. When the going gets tough, the tough call for close air support.]],[[37. There is no 'overkill.' There is only 'open fire' and 'reload.']],[[38. What's easy for you can still be hard on your clients.]],[[39. There is a difference between "spare" parts and "extra" parts.]],[[40. Not all good news is enemy action.]],[[41. "Do you have a backup?" means "I can't fix this."]],[[42. "They'll never expect this" means "I want to try something stupid".]],[[43. If it's stupid and it works, it's still stupid and you're lucky.]],[[44. If it will blow a hole in the ground, it will double as an entrenching tool.]],[[45. The size of the combat bonus is inversely proportional to the likelihood of surviving to collect it.]],[[46. Don't try to save money by conserving ammunition.]],[[47. Don't expect the enemy to cooperate in the creation of your dream engagement.]],[[48. If it ain't broke, it hasn't been issued to the infantry.]],[[49. Every client is one missed payment from becoming a target.]],[[50. Every target is one bribe away from becoming a client.]],[[51. Let them see you sharpen the sword before you fall on it.]],[[52. The army you've got is never the army you want.]],[[53. The intel you've got is never the intel you want.]],[[54. It's only too many troops if you can't pay them.]],[[55. It's only too many weapons if they're pointing in the wrong direction.]],[[56. Infantry exists to paint targets for people with real guns.]],[[57. Artillery exists to launch large chunks of budget at an enemy it cannot actually see.]],[[58. The pen is mightiest when it writes orders for swords.]],[[59. Two wrongs is probably not going to be enough.]],[[60. Any weapon's rate of fire is inversely proportional to the number of available targets.]],[[61. Don't bring big grenades into small rooms.]],[[62. Anything labeled "This End Toward Enemy" is dangerous at both ends.]],[[63. The brass knows how to do it by knowing who can do it.]],[[64. An ounce of sniper is worth a pound of suppressing fire.]],[[65. After the toss, be the one with the pin, not the one with the grenade.]],[[66. Necessity is the mother of deception.]],[[67. If you can't carry cash, carry a weapon.]],[[68. Negotiating from a position of strength does not mean you shouldn't also negotiate from a position near the exits.]],[[69. Sometimes rank is a function of firepower.]],[[70. Failure is not an option. It is mandatory. The option is whether or not to let failure be the last thing you do.]],}
553
554local function motivate(A) return tQuotes[A or math.random(#tQuotes)] end util["motivate"] = motivate
555
556local function saneTime(nTime,nOh,nOm)
557 if type( nTime ) ~= "number" then nTime = os.time() end
558 local nHour = math.floor(nTime) + (nOh or 0)
559 local nMinute = math.floor((nTime - nHour)*60) + (nOm or 0)
560 return string.format( "%02d:%02d", nHour, nMinute )
561end util["saneTime"] = saneTime
562
563local function saneEpoch(nTime)
564 if type( nTime ) ~= "number" then nTime = os.epoch("utc") end
565 return math.floor(nTime/1000)
566end util["saneEpoch"] = saneEpoch
567
568local function wait(nTime)
569 if type( nTime ) ~= "number" then
570 error( "bad argument #1 (expected number, got " .. type( nTime ) .. ")", 2 )
571 end
572 local x,y = term.getCursorPos()
573 local endTimer = os.startTimer(nTime)
574 local endTime = os.clock() + nTime
575 local updateTimer = os.startTimer(0)
576 local event
577 while true do
578 event = {os.pullEvent()}
579 if event[1] == "timer" then
580 if event[2] == endTimer then break
581 elseif event[2] == updateTimer then
582 updateTimer = os.startTimer(0.1)
583 term.setCursorPos(1,y)
584 term.clearLine()
585 term.write( string.format( "%f" , math.floor((endTime-os.clock())*10)/10 ) )
586 end
587 end
588 end
589 term.setCursorPos(1,y)
590 term.clearLine()
591end util["wait"] = wait
592
593local function waitBar(nTime)
594 if type( nTime ) ~= "number" then
595 error( "bad argument #1 (expected number, got " .. type( nTime ) .. ")", 2 )
596 end
597 local sX,sY = term.getSize()
598 local x,y = term.getCursorPos()
599 local endTimer = os.startTimer(nTime)
600 local endTime = os.clock() + nTime
601 local updateInter = math.max(nTime/(sX*2),0.05)
602 local updateTimer = os.startTimer(0)
603 local size = -1
604 local event
605 while true do
606 event = {os.pullEvent()}
607 if event[1] == "timer" then
608 if event[2] == endTimer then break
609 elseif event[2] == updateTimer then
610 updateTimer = os.startTimer(updateInter)
611 size = math.floor( ( endTime - os.clock() )*sX*2 / nTime )
612 term.setCursorPos(1,y)
613 term.clearLine()
614 term.write( string.rep( "\140" , math.floor(size/2) ) )
615 if size%2 == 1 then term.write( "\132" ) end
616 end
617 end
618 end
619 term.setCursorPos(1,y)
620 term.clearLine()
621end util["waitBar"] = waitBar
622
623local function explode(sString,sDiv)
624 if sDiv == "" then return {sString} end
625 local tOut = {}
626 local nPos = 0
627 local fSearcher = function() return string.find(sString,sDiv,nPos,false) end
628 for nStart,nStop in fSearcher do
629 table.insert(tOut,string.sub(sString,nPos,nStart-1))
630 nPos = nStop + 1
631 end
632 table.insert(tOut,string.sub(sString,nPos))
633 return tOut
634end
635
636return util