<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<el:level xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://enigma-game.org/schema/level/1 level.xsd" xmlns:el="http://enigma-game.org/schema/level/1">
  <el:protected>
    <el:info el:type="library">
      <el:identity el:title="" el:id="lib/liblua"/>
      <el:version el:score="1" el:release="1" el:revision="7" el:status="released"/>
      <el:author  el:name="Enigma Team" el:email="" el:homepage=""/>
      <el:copyright>Copyright © 2007, 2008 Enigma Team</el:copyright>
      <el:license el:type="GPL v2.0 or above" el:open="true"/>
      <el:compatibility el:enigma="1.10">
      </el:compatibility>
      <el:modes el:easy="false" el:single="false" el:network="false"/>
      <el:comments>
      </el:comments>
      <el:score el:easy="-" el:difficult="-"/>
    </el:info>
    <el:luamain><![CDATA[

---------------------------------------------------------------------
-- liblua holds some general utilities for working with Lua.
-- It includes functions for work with tables (deep-copy, combine,
-- shuffle, print tables), a modulo wrapper and general debugging
-- functions (currently only to_string).
---------------------------------------------------------------------
--
-- liblua provides the following functions:
--   lib.lua.deep_copy(source)
--   lib.lua.combine_tables(arg1, ...)
--   lib.lua.shuffle(t)
--   lib.lua.print_table(t, prefix, depth)
--   lib.lua.to_string(thing)
--   lib.lua.mod(value, modul)
--

lib.lua = {}
setmetatable(lib.lua, getmetatable(lib))

---------------------------------------------------------------------
--  TABLE  HANDLING
---------------------------------------------------------------------

-- deep_copy returns a copy of SOURCE, where table entries are
-- not copied as memory references, but complete ("deep copy").
-- Metatables are transfered, but not deep-copied.
function lib.lua.deep_copy(source)
    if type(source) ~= "table" then
        return source
    end
    local dest = {}
    for k, v in pairs(source) do
        if type(v) == "table" then
            rawset(dest, k, lib.lua.deep_copy(v))
        else
            rawset(dest, k, v)
        end
    end
    setmetatable(dest, getmetatable(source))
    return dest
end 

-- combine_tables returns a table consisting of all entries of the
-- entries of OVER_TABLE: OVER_TABLE is a table of tables, say
-- {T1, T2, T3, ...}. The result of combine_tables will be a new
-- table with all entries of T1, T2, T3 etc., with the first table
-- having highest priority etc.
function lib.lua.combine_tables(arg1, ...)
    local args = {arg1, ...}
    if table.getn(args) == 1 then
        args = arg1
    end
    if type(args) ~= "table" then
        error("combine_tables: None or only one argument, and it's not a table!", 2)
    end
    local result = {}
    for j,t in pairs(args) do
        if type(t) ~= "table" then
            error("combine_tables: Main table does not consist of tables alone!", 2)
        end
        for k,v in pairs(t) do
            result[k] = result[k] or lib.lua.deep_copy(v)
        end
    end
    return result
end

-- shuffle resorts the table T randomly and returns a shallow copy of the
-- shuffled table. Note that only those entries of T can be sorted, that
-- are indexed with integers from 1 to table.getn(T), all other entries
-- will be in the result, but will not be shuffled. Metatable will be
-- that of the argument. Metamethods will be used during the copy
-- (i.e. no rawget or rawset).
function lib.lua.shuffle(t)
    if type(t) ~= "table" then
        error("lib.lua.shuffle: Argument is not a table.", 2)
    end
    local result = {}
    setmetatable(result, getmetatable(t))
    for key, value in pairs(t) do
        result[key] = value
    end
    if table.getn(result) < 2 then
        return result
    end
    for n = table.getn(result), 2, -1 do
        local m = math.random(n)
        result[n], result[m] = result[m], result[n]
    end
    return result
end

-- print_table uses the print command to print all
-- entries of a table, one table per line.
-- It should be used for debug reasons only.
-- PREFIX can be a string to be put in front of each
-- line of the output, in case you need to distinguish
-- several outputs from each other.
-- print_table is recursive, i.e. a table with table
-- as entries will call print_table again. To avoid
-- infinite loops, DEPTH is used as additional argument.
-- Don't use it in levels. If you want to suppress
-- the recursive function, use DEPTH = -1.
function lib.lua.print_table(t, prefix, depth)
    if type(t) ~= "table" then
        print("print_table: Argument is of type "..type(t)..", not table.")
        return
    end
    for key, value in pairs(t) do
        local key_s
        if type(key) == "string" then
            key_s = "\"" .. key .. "\""
        elseif type(key) == "number" then
            key_s = key
        elseif usertype(key) == "position" then
            key_s = "<" .. key.x .. "/" .. key.y .. ">"
        elseif type(key) == "userdata" then
            key_s = "<" .. usertype(key) .. ">"
        else
            key_s = "<" .. type(key) .. ">"
        end
        if type(value) == "number" then
            print((prefix or "") .. "|" .. key_s .. " = " .. value)
        elseif type(value) == "string" then
            print((prefix or "") .. "|" .. key_s .. " = \"" .. value .. "\"")
        elseif type(value) == "boolean" then
            print((prefix or "") .. "|" .. key_s .. " = " .. cond(value, "true", "false"))
        elseif type(value) == "table" then
            if (depth or 0) < 5 then
                print((prefix or "") .. "|" .. key_s .. " = |")
                lib.lua.print_table(value,
                    (prefix or "") .. "|" .. string.rep(" ", string.len(key_s) + 3),
                    (depth or 0) + 1)
                print((prefix or "") .. "|")
            else
                print((prefix or "") .. "|" .. key_s .. " = | ...")
            end
        elseif usertype(value) == "position" then
            print((prefix or "") .. "|" .. key_s .. " = position " .. value.x
                  .. "/" .. value.y)
        elseif usertype(value) == "polist" then
            print((prefix or "") .. "|" .. key_s .. " = polist of " .. #value
                  .. " position(s)")
        elseif usertype(value) == "object" then
            print((prefix or "") .. "|" .. key_s .. " = object of name '"
                  .. (value.name or "") .. "'")
        else
            print((prefix or "") .. "|" .. key_s .. " of type " .. type(value)
                  .. " (usertype '" .. usertype(value) .. "')")
        end
    end
end

function lib.lua.to_string(thing)
    local t = etype(thing)
    local result = t
    if (t == "nil") then
        result = "nil"
    elseif (t == "string") then
        if string.find(thing, "\n", 1, true) then
            result = "string of several lines: \n" .. thing
        end
        result = "string: \"" .. thing .. "\""
    elseif (t == "number") then
        result = "number: " .. thing
    elseif (t == "boolean") then
        result = "boolean: " .. cond(thing, "true", "false")
    elseif (t == "table") then
        result = "table:\n"
        local found = false
        for key, value in pairs(thing) do
            found = true
            result = result .. "  " .. lib.lua.to_string(key) .. " -> " .. lib.lua.to_string(value) .. "\n"
        end
        if not found then
            result = "empty table"
        end
    elseif (t == "position") then
        result = "position: " .. thing.x .. "/" .. thing.y
    elseif (t == "object") then
        result = "object with name '" .. thing.name .. "' at " .. lib.lua.to_string(po(thing))
    elseif (t == "tile") then
        result = "tile"
    elseif (t == "tiles") then
        result = "tiles"
    elseif (t == "group") then
        result = "object group with " .. #thing .. " objects:\n"
        for obj in thing do
            result = result .. "  " .. lib.lua.to_string(obj) .. "\n"
        end
    elseif (t == "polist") then
        result = "position list with " .. #thing .. " positions:\n"
        for j = 1, #thing do
            result = result .. "  " .. j .. ": " .. thing[j].x .. "/" .. thing[j].y .. "\n"
        end
    elseif (t == "map") then
        result = "map of size " .. thing.width .. " x " .. thing.height .. " with default key '"
                   .. thing.defaultkey .. "':\n"
        for j = 1, thing.width do
            result = result .. "  \"" .. thing[j] .. "\"\n"
        end
    elseif (t == "unknown") then
        result = "unknown userdata"
    end
    return result
end

---------------------------------------------------------------------
--  MATHEMATICAL  FUNCTIONS
---------------------------------------------------------------------

-- As Lua uses different names for the modulo-function in its
-- versions, it's sometimes better to wrap them.
-- Even worse, lua's modulo function doesn't handle negative
-- values as it should. The following function returns (given
-- an integer) another integer between 0 and MODUL-1.
function lib.lua.mod(value, modul)
    assert_type(value, "lib.lua.mod first argument", 2, "number")
    assert_type(modul, "lib.lua.mod second argument", 2, "positive")
    if value < 0 then
        -- No, the following call to lib.lua.mod is not a real recursion, it's
        -- only for the case where VALUE is a negative multiple of MODUL
        -- (otherwise we would get MODUL as result, not zero).
        return lib.lua.mod(modul + (math.fmod or math.mod)(value, modul), modul)
    else
        return (math.fmod or math.mod)(value, modul)
    end
end

    ]]></el:luamain>
    <el:i18n>
    </el:i18n>
  </el:protected>
</el:level>

