While running the tutorial, the Save command is disabled. When the tutorial was extended to two levels, stashing and restoring the hero's equipment stopped working as intended if player entered the second level. The attempted fix for that broke re-enabling Save even if the player left the tutorial without entering its second level. This seems to fix things, but I'm flailing around with barely a clue here. A couple of simpler attempts didn't work and I haven't figured out why, so this is a bit more complex than what I wanted. Reorganizing nhl_callback() isn't part of the fix, just avoids use of some redundant code.
243 lines
6.9 KiB
Lua
243 lines
6.9 KiB
Lua
-- 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
|