-- Lives in the dream -- ------------------ -- << misc.lua >> -- 雑ルーチン集 -- Copyright(c)2003-2004 yuno -- 危険な香りのする標準関数を封じる。 rawset(_G, "loadlib", nil) io = {} os = { clock = os.clock, date = os.date, difftime = os.difftime, getenv = os.getenv, time = os.time } debug.debug = nil -- ランダムジェネレータの初期化。 math.randomseed(os.time()) -- 代替出力先。 function altprint() end -- 標準の print を置き換えて出力を syslog に吐く。 function print(...) for i = 1, arg.n do arg[i] = tostring(arg[i]) end local s = table.concat(arg, "\t") syslog(LOG_DEBUG, "print: %s", s) altprint(s) end -- 標準の assert を置き換えてエラーメッセージを syslog に吐く。 function assert(v, msg) if not v then msg = tostring(msg or "assertion failed!") syslog(LOG_ERR, "lua assert: %s", tostring(msg)) error(msg, 2) end return v end -- syslog function syslog(level, ...) return dream.syslog(level, string.format(unpack(arg))) end -- unpack の逆。引数をリスト化する。 -- 不定個の戻り値を保存するために使う。 function pack(...) return arg end -- 文字が10進数文字か調べる。 function isdigit(c) return string.find(c, "%d") end -- 文字列をエスケープする。 function escape(s) if not string.find(s, "[%z\1-\31\\\"]") then return s else local ret = "" local digit = false for i = 1, string.len(s) do local c = string.sub(s, i, i) if c == "\\" then ret = ret.."\\\\" digit = false elseif c == "\"" then ret = ret.."\\\"" digit = false elseif c == "\a" then ret = ret.."\\a" digit = false elseif c == "\b" then ret = ret.."\\b" digit = false elseif c == "\f" then ret = ret.."\\f" digit = false elseif c == "\n" then ret = ret.."\\n" digit = false elseif c == "\r" then ret = ret.."\\r" digit = false elseif c == "\t" then ret = ret.."\\t" digit = false elseif c == "\v" then ret = ret.."\\v" digit = false else local b = string.byte(c) if b < 32 or (digit and isdigit(c)) then ret = ret.."\\"..tostring(b) digit = (b < 100) else ret = ret..c digit = false end end end return ret end end -- 文字列をクォートする。 function quote(value) local t = type(value) if t == "string" then return "\""..escape(value).."\"" elseif t == "number" then local s = tostring(value) if pcall(function() return tonumber(s) end) then return s end else return tostring(value) end end -- リストを文字列に展開する。 function quoteList(t) t = pack(unpack(t)) for i = 1, t.n do t[i] = quote(t[i]) end return "{"..table.concat(t, ", ").."}" end -- 16進数文字列を数値に変換。 function hex(s) return tonumber(s, 16) end -- 文字列を16進ダンプに変換する。 -- 例: "ABC" --> "414243" function hexdump(s) local ret = {} for i = 1, string.len(s) do ret[i] = string.format("%02X", string.byte(s, i)) end return table.concat(ret) end -- __indexを辿るpairs function allpairs(t) local r = {} local function scan(t) for key, value in pairs(t) do if r[key] == nil then r[key] = value end end local mt = getmetatable(t) if mt then if mt.__index then return scan(mt.__index) end end end scan(t) return pairs(r) end -- 6面体ダイスをn個振る合計。 function dice(n) local ret = 0 for i = 1, n do n = n + math.random(6) end return ret end -- dir2xyで使うテーブル local dir2xy_table = { S = function() return 0,1 end, SW = function() return -1,1 end, W = function() return -1,0 end, NW = function() return -1,-1 end, N = function() return 0,-1 end, NE = function() return 1,-1 end, E = function() return 1,0 end, SE = function() return 1,1 end } -- 方向をXYに function dir2xy(dir) return dir2xy_table[dir]() end -- XYを方向に function xy2dir(x, y) if y < 0 then if x < 0 then return "NW" elseif x > 0 then return "NE" else return "N" end elseif y > 0 then if x < 0 then return "SW" elseif x > 0 then return "SE" else return "S" end elseif x < 0 then return "W" elseif x > 0 then return "E" end end -- 距離を求める function distance(ax, ay, aw, ah, bx, by, bw, bh) local ar, ab, br, bb = ax + aw, ay + ah, bx + bw, by + bh local dx, dy if ar <= bx then dx = bx - ar + 1 elseif br <= ax then dx = ax - br + 1 else dx = 0 end if ab <= by then dy = by - ab + 1 elseif bb <= ay then dy = ay - bb + 1 else dy = 0 end return math.sqrt(dx * dx + dy * dy) end -- エンティティ間の距離を求める。 function entityDistance(a, b) if a.area and a.area == b.area then local ax, ay, bx, by = a.x, a.y, b.x, b.y local ar, ab, br, bb = ax + a.width, ay + a.height, bx + b.width, by + b.height local dx, dy if ar <= bx then dx = bx - ar + 1 elseif br <= ax then dx = ax - br + 1 else dx = 0 end if ab <= by then dy = by - ab + 1 elseif bb <= ay then dy = ay - bb + 1 else dy = 0 end return math.sqrt(dx * dx + dy * dy) else return 99999 end end -- 時間"YYYYMMDDhhmmss"の差を求める function difftime(a, b) local mdays = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 } local function parsetime(s) local year = tonumber(string.sub(s, 1, 4)) local mon = tonumber(string.sub(s, 5, 6)) local day = tonumber(string.sub(s, 7, 8)) local hour = tonumber(string.sub(s, 9, 10)) local min = tonumber(string.sub(s, 11, 12)) local sec = tonumber(string.sub(s, 13, 14)) local yday = (day - 1) + mdays[mon] if mon > 2 and (math.mod(year, 400) == 0 or (math.mod(year, 100) ~= 0 and math.mod(year, 4) == 0)) then yday = yday + 1 end local yearDays = (year - 2000) * 365 + math.floor((year - 1) / 4) - math.floor((year - 1) / 100) + math.floor((year - 1) / 400) return sec + min * 60 + hour * 3600 + (yday + yearDays) * 86400 end return parsetime(b) - parsetime(a) end -- 現在時刻を"YYYYMMDDhhmmss"で返す function now() return os.date("!%Y%m%d%H%M%S") end -- エラートラップ付き関数呼び出し function safecall(f, ...) if f then local status, ret = xpcall(function() return pack(f(unpack(arg))) end, debug.traceback) if status then return unpack(ret) else syslog(LOG_ERR, "%s", ret) end end end -- 制限時間付き関数呼び出し -- fを実行してt秒経過するとエラーを起こす。 function alarmcall(t, f, ...) local expires = dream.clock() + t local function hook() if dream.clock() >= expires then error("alarmcall: limit time expired", 2) end end local co = coroutine.wrap(function() debug.sethook(hook, "", 100000) -- フックはコルーチン内でだけ有効 return xpcall(function() return f(unpack(arg)) end, debug.traceback) end) return co() end -- end of file --