-- NetHack nhlib.lua	$NHDT-Date: 1652196140 2022/05/10 15:22:20 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.4 $
--	Copyright (c) 2021 by Pasi Kallinen
-- NetHack may be freely redistributed.  See license for details.
-- compatibility shim
math.random = function(...)
   local arg = {...};
   if (#arg == 1) then
      return 1 + nh.rn2(arg[1]);
   elseif (#arg == 2) then
      return nh.random(arg[1], arg[2] + 1 - arg[1]);
   else
      -- we don't support reals
      error("NetHack math.random requires at least one parameter");
   end
end

function shuffle(list)
   for i = #list, 2, -1 do
      local j = math.random(i)
      list[i], list[j] = list[j], list[i]
   end
end

align = { "law", "neutral", "chaos" };
shuffle(align);

-- d(2,6) = 2d6
-- d(20) = 1d20 (single argument = implicit 1 die)
function d(dice, faces)
   if (faces == nil) then
      -- 1-arg form: argument "dice" is actually the number of faces
      return math.random(1, dice)
   else
      local sum = 0
      for i=1,dice do
         sum = sum + math.random(1, faces)
      end
      return sum
   end
end

-- percent(20) returns true 20% of the time
function percent(threshold)
   return math.random(0, 99) < threshold
end

function monkfoodshop()
   if (u.role == "Monk") then
      return "health food shop";
   end
   return "food shop";
end

-- tweaks to gehennom levels; might add random lava pools or
-- a lava river.
-- protected_area is a selection where no changes will be done.
function hell_tweaks(protected_area)
   local liquid = "L";
   local ground = ".";
   local n_prot = protected_area:numpoints();
   local prot = protected_area:negate();

   -- random pools
   if (percent(20 + u.depth)) then
      local pools = selection.new();
      local maxpools = 5 + math.random(u.depth);
      for i = 1, maxpools do
         pools:set();
      end
      pools = pools | selection.grow(selection.set(selection.new()), "west")
      pools = pools | selection.grow(selection.set(selection.new()), "north")
      pools = pools | selection.grow(selection.set(selection.new()), "random")

      pools = pools & prot;

      if (percent(80)) then
         local poolground = pools:clone():grow("all") & prot;
         local pval = math.random(1, 8) * 10;
         des.terrain(poolground:percentage(pval), ground)
      end
      des.terrain(pools, liquid)
   end

   -- river
   if (percent(50)) then
      local allrivers = selection.new();
      local reqpts = ((nhc.COLNO * nhc.ROWNO) - n_prot) / 12; -- # of lava pools required
      local rpts = 0;
      local rivertries = 0;

      repeat
            local floor = selection.match(ground);
            local a = selection.rndcoord(floor);
            local b = selection.rndcoord(floor);
            local lavariver = selection.randline(selection.new(), a.x, a.y, b.x, b.y, 10);

            if (percent(50)) then
               lavariver = selection.grow(lavariver, "north");
            end
            if (percent(50)) then
               lavariver = selection.grow(lavariver, "west");
            end
            allrivers = allrivers | lavariver;
            allrivers = allrivers & prot;

            rpts = allrivers:numpoints();
            rivertries = rivertries + 1;
      until ((rpts > reqpts) or (rivertries > 7));

      if (percent(60)) then
         local prc = 10 * math.random(1, 6);
         local riverbanks = selection.grow(allrivers);
         riverbanks = riverbanks & prot;
         des.terrain(selection.percentage(riverbanks, prc), ground);
      end

      des.terrain(allrivers, liquid);
   end

   -- replacing some walls with boulders
   if (percent(20)) then
      local amount = 3 * math.random(1, 8);
      local bwalls = selection.match([[.w.]]):percentage(amount) | selection.match(".\nw\n."):percentage(amount);
      bwalls = bwalls & prot;
      bwalls:iterate(function (x,y)
            des.terrain(x, y, ".");
            des.object("boulder", x, y);
      end);
   end

   -- replacing some walls with iron bars
   if (percent(20)) then
      local amount = 3 * math.random(1, 8);
      local fwalls = selection.match([[.w.]]):percentage(amount) | selection.match(".\nw\n."):percentage(amount);
      fwalls = fwalls:grow() & selection.match("w") & prot;
      des.terrain(fwalls, "F");
   end

end

-- pline with variable number of arguments
function pline(fmt, ...)
   nh.pline(string.format(fmt, table.unpack({...})));
end

-- wrapper to make calling from nethack core easier
function nh_set_variables_string(key, tbl)
   return "nh_lua_variables[\"" .. key .. "\"]=" .. table_stringify(tbl) .. ";";
end

-- wrapper to make calling from nethack core easier
function nh_get_variables_string(tbl)
   return "return " .. table_stringify(tbl) .. ";";
end

-- return the (simple) table tbl converted into a string
function table_stringify(tbl)
   local str = "";
   for key, value in pairs(tbl) do
      local typ = type(value);
      if (typ == "table") then
         str = str .. "[\"" .. key .. "\"]=" .. table_stringify(value);
      elseif (typ == "string") then
         str = str .. "[\"" .. key .. "\"]=[[" .. value .. "]]";
      elseif (typ == "boolean") then
         str = str .. "[\"" .. key .. "\"]=" .. tostring(value);
      elseif (typ == "number") then
         str = str .. "[\"" .. key .. "\"]=" .. value;
      elseif (typ == "nil") then
         str = str .. "[\"" .. key .. "\"]=nil";
      end
      str = str .. ",";
   end
   -- pline("table_stringify:(%s)", str);
   return "{" .. str .. "}";
end

--
-- TUTORIAL
--

-- extended commands NOT available in tutorial
local tutorial_blacklist_commands = {
   ["save"] = true,
};

function tutorial_cmd_before(cmd)
   -- nh.pline("TUT:cmd_before:" .. cmd);

   if (tutorial_blacklist_commands[cmd]) then
      return false;
   end
   return true;
end

function tutorial_enter()
   -- nh.pline("TUT:enter");

   -- add the tutorial branch callbacks
   nh.callback("cmd_before", "tutorial_cmd_before");
   nh.callback("end_turn", "tutorial_turn");

   -- save state for later restore
   nh.gamestate();
end

function tutorial_leave()
   -- nh.pline("TUT:leave");

   -- remove the tutorial branch callbacks
   nh.callback("cmd_before", "tutorial_cmd_before", true);
   nh.callback("end_turn", "tutorial_turn", true);

   -- restore state for regular play
   nh.gamestate(true);
end

local tutorial_events = {
   {
      func = function()
         if (u.uhunger < 148) then
            local o = obj.new("blessed food ration");
            o:placeobj(u.ux, u.uy);
            nh.pline("Looks like you're getting hungry.  You'll starve to death, unless you eat something.", true);
            nh.pline("Comestibles are eaten with '" .. nh.eckey("eat") .. "'", true);
            return true;
         end
      end
   },
};

function tutorial_turn()
   for k, v in pairs(tutorial_events) do
      if ((v.ucoord and u.ux == v.ucoord[1] + 3 and u.uy == v.ucoord[2] + 3)
         or (v.ucoord == nil)) then
         if (v.func() or v.remove) then
            tutorial_events[k] = nil;
         end
      end
   end
   -- nh.pline("TUT:turn");
end
