Adds a new lua command
des.exclusion({ type = "teleport", region = { x1,y1, x2,y2 } });
which allows defining "exclusion zones" in the level, areas where
random teleports (or falling into the level) will never place the hero.
Does not prevent targeted teleportation into the area.
Breaks saves and bones.
423 lines
11 KiB
Lua
423 lines
11 KiB
Lua
-- NetHack 3.7 hellfill.des $NHDT-Date: 1432512783 2015/05/25 00:13:03 $ $NHDT-Branch: master $:$NHDT-Revision: 1.25 $
|
|
-- Copyright (c) 2022 by Pasi Kallinen
|
|
-- NetHack may be freely redistributed. See license for details.
|
|
--
|
|
--
|
|
|
|
-- The "fill" level for gehennom.
|
|
--
|
|
-- This level is used to fill out any levels not occupied by
|
|
-- specific levels.
|
|
--
|
|
|
|
function hellobjects()
|
|
local objclass = { "(", "/", "=", "+", ")", "[", "?", "*", "%" };
|
|
shuffle(objclass);
|
|
|
|
des.object(objclass[1]);
|
|
des.object(objclass[1]);
|
|
des.object(objclass[2]);
|
|
des.object(objclass[3]);
|
|
des.object(objclass[4]);
|
|
des.object(objclass[5]);
|
|
des.object()
|
|
des.object()
|
|
end
|
|
|
|
--
|
|
|
|
function hellmonsters()
|
|
local monclass = { "V", "D", " ", "&", "Z" };
|
|
shuffle(monclass);
|
|
|
|
des.monster({ class = monclass[1], peaceful = 0 });
|
|
des.monster({ class = monclass[1], peaceful = 0 });
|
|
des.monster({ class = monclass[2], peaceful = 0 });
|
|
des.monster({ class = monclass[2], peaceful = 0 });
|
|
des.monster({ class = monclass[3], peaceful = 0 });
|
|
des.monster({ class = monclass[4], peaceful = 0 });
|
|
des.monster({ peaceful = 0 });
|
|
des.monster({ class = "H", peaceful = 0 });
|
|
end
|
|
|
|
--
|
|
|
|
function helltraps()
|
|
for i = 1, 12 do
|
|
des.trap()
|
|
end
|
|
end
|
|
|
|
--
|
|
|
|
function populatemaze()
|
|
for i = 1, math.random(8) + 11 do
|
|
if (percent(50)) then
|
|
des.object("*");
|
|
else
|
|
des.object();
|
|
end
|
|
end
|
|
|
|
for i = 1, math.random(10) + 2 do
|
|
des.object("`");
|
|
end
|
|
|
|
for i = 1, math.random(3) do
|
|
des.monster({ id = "minotaur", peaceful = 0 });
|
|
end
|
|
|
|
for i = 1, math.random(5) + 7 do
|
|
des.monster({ peaceful = 0 });
|
|
end
|
|
|
|
for i = 1, math.random(6) + 7 do
|
|
des.gold();
|
|
end
|
|
|
|
for i = 1, math.random(6) + 7 do
|
|
des.trap();
|
|
end
|
|
end
|
|
|
|
--
|
|
|
|
function rnd_halign()
|
|
local aligns = { "half-left", "center", "half-right" };
|
|
return aligns[math.random(1, #aligns)];
|
|
end
|
|
|
|
function rnd_valign()
|
|
local aligns = { "top", "center", "bottom" };
|
|
return aligns[math.random(1, #aligns)];
|
|
end
|
|
|
|
-- the prefab maps must have contents-function, or populatemaze()
|
|
-- puts the stuff only inside the prefab map.
|
|
-- contains either a function, or an object with "repeatable" and "contents".
|
|
-- function alone implies not repeatable.
|
|
local hell_prefabs = {
|
|
{
|
|
repeatable = true,
|
|
contents = function ()
|
|
des.map({ halign = rnd_halign(), valign = "center", map = [[
|
|
......
|
|
......
|
|
......
|
|
......
|
|
......
|
|
......
|
|
......
|
|
......
|
|
......
|
|
......
|
|
......
|
|
......
|
|
......
|
|
......
|
|
......
|
|
......]], contents = function() end });
|
|
end
|
|
},
|
|
{
|
|
repeatable = true,
|
|
contents = function ()
|
|
des.map({ halign = rnd_halign(), valign = "center", map = [[
|
|
xxxxxx.....xxxxxx
|
|
xxxx.........xxxx
|
|
xx.............xx
|
|
xx.............xx
|
|
x...............x
|
|
x...............x
|
|
.................
|
|
.................
|
|
.................
|
|
.................
|
|
.................
|
|
x...............x
|
|
x...............x
|
|
xx.............xx
|
|
xx.............xx
|
|
xxxx.........xxxx
|
|
xxxxxx.....xxxxxx
|
|
]], contents = function() end });
|
|
end
|
|
},
|
|
function (coldhell)
|
|
des.map({ halign = rnd_halign(), valign = rnd_valign(), map = [[
|
|
xxxxxx.xxxxxx
|
|
xLLLLLLLLLLLx
|
|
xL---------Lx
|
|
xL|.......|Lx
|
|
xL|.......|Lx
|
|
.L|.......|L.
|
|
xL|.......|Lx
|
|
xL|.......|Lx
|
|
xL---------Lx
|
|
xLLLLLLLLLLLx
|
|
xxxxxx.xxxxxx
|
|
]], contents = function()
|
|
des.non_diggable(selection.area(2,2, 10,8));
|
|
des.region(selection.area(4,4, 8,6), "lit");
|
|
des.exclusion({ type = "teleport", region = { 2,2, 10,8 } });
|
|
if (coldhell) then
|
|
des.replace_terrain({ region = {1,1, 11,9}, fromterrain="L", toterrain="P" });
|
|
end
|
|
local dblocs = {
|
|
{ x = 1, y = 5, dir="east", state="closed" },
|
|
{ x = 11, y = 5, dir="west", state="closed" },
|
|
{ x = 6, y = 1, dir="south", state="closed" },
|
|
{ x = 6, y = 9, dir="north", state="closed" }
|
|
}
|
|
shuffle(dblocs);
|
|
for i = 1, math.random(1, #dblocs) do
|
|
des.drawbridge(dblocs[i]);
|
|
end
|
|
local mons = { "H", "T", "@" };
|
|
shuffle(mons);
|
|
for i = 1, 3 + math.random(1, 5) do
|
|
des.monster(mons[1], 6, 5);
|
|
end
|
|
end });
|
|
end,
|
|
{
|
|
repeatable = true,
|
|
contents = function ()
|
|
des.map({ halign = "center", valign = "center", map = [[
|
|
..............................................................
|
|
..............................................................
|
|
..............................................................
|
|
..............................................................
|
|
..............................................................]], contents = function() end });
|
|
end
|
|
},
|
|
{
|
|
repeatable = true,
|
|
contents = function ()
|
|
des.map({ halign = rnd_halign(), valign = rnd_valign(), lit = true, map = [[
|
|
x.....x
|
|
.......
|
|
.......
|
|
.......
|
|
.......
|
|
.......
|
|
x.....x]], contents = function() end });
|
|
end
|
|
},
|
|
function ()
|
|
des.map({ halign = rnd_halign(), valign = rnd_valign(), map = [[
|
|
BBBBBBB
|
|
B.....B
|
|
B.....B
|
|
B.....B
|
|
B.....B
|
|
B.....B
|
|
BBBBBBB]], contents = function()
|
|
des.region({ region={2,2, 2,2}, type="temple", filled=1, irregular=1 });
|
|
des.altar({ x=3, y=3, align="noalign", type=percent(75) and "altar" or "shrine" });
|
|
end });
|
|
end,
|
|
function ()
|
|
des.map({ halign = rnd_halign(), valign = rnd_valign(), map = [[
|
|
..........
|
|
..........
|
|
..........
|
|
...FFFF...
|
|
...F..F...
|
|
...F..F...
|
|
...FFFF...
|
|
..........
|
|
..........
|
|
..........]], contents = function()
|
|
des.exclusion({ type = "teleport", region = { 4,4, 5,5 } });
|
|
local mons = { "Angel", "D", "H", "L" };
|
|
des.monster(mons[math.random(1, #mons)], 4,4);
|
|
end });
|
|
end,
|
|
|
|
function ()
|
|
des.map({ halign = rnd_halign(), valign = rnd_valign(), map = [[
|
|
.........
|
|
.}}}}}}}.
|
|
.}}---}}.
|
|
.}--.--}.
|
|
.}|...|}.
|
|
.}--.--}.
|
|
.}}---}}.
|
|
.}}}}}}}.
|
|
.........
|
|
]], contents = function(rm)
|
|
des.exclusion({ type = "teleport", region = { 3,3, 5,5 } });
|
|
des.monster("L",04,04)
|
|
end })
|
|
end,
|
|
function ()
|
|
local mapstr = percent(30) and [[
|
|
.....
|
|
.LLL.
|
|
.LZL.
|
|
.LLL.
|
|
.....]] or [[
|
|
.....
|
|
.PPP.
|
|
.PWP.
|
|
.PPP.
|
|
.....]];
|
|
for dx = 1, 5 do
|
|
des.map({ x = dx*14 - 4, y = math.random(3, 15),
|
|
map = mapstr, contents = function() end })
|
|
end
|
|
end,
|
|
{
|
|
repeatable = true,
|
|
contents = function ()
|
|
local mapstr = [[
|
|
...
|
|
...
|
|
...
|
|
...
|
|
...
|
|
...
|
|
...
|
|
...
|
|
...
|
|
...
|
|
...
|
|
...
|
|
...
|
|
...
|
|
...
|
|
...
|
|
...]];
|
|
for dx = 1, 3 do
|
|
des.map({ x = math.random(3, 75), y = 3,
|
|
map = mapstr, contents = function() end })
|
|
end
|
|
end
|
|
},
|
|
};
|
|
|
|
function rnd_hell_prefab(coldhell)
|
|
local dorepeat = true;
|
|
local nloops = 0;
|
|
repeat
|
|
nloops = nloops + 1;
|
|
local pf = math.random(1, #hell_prefabs);
|
|
local fab = hell_prefabs[pf];
|
|
local fabtype = type(fab);
|
|
|
|
if (fabtype == "function") then
|
|
fab(coldhell);
|
|
dorepeat = false;
|
|
elseif (fabtype == "table") then
|
|
fab.contents(coldhell);
|
|
dorepeat = not (fab.repeatable and math.random(0, nloops * 2) == 0);
|
|
end
|
|
until ((not dorepeat) or (nloops > 5));
|
|
end
|
|
|
|
hells = {
|
|
-- 1: "mines" style with lava
|
|
function ()
|
|
des.level_init({ style = "solidfill", fg = " ", lit = 0 });
|
|
des.level_flags("mazelevel", "noflip");
|
|
des.level_init({ style="mines", fg=".", smoothed=true ,joined=true, lit=0, walled=true });
|
|
des.replace_terrain({ fromterrain = " ", toterrain = "L" });
|
|
des.replace_terrain({ fromterrain = ".", toterrain = "L", chance = 5 });
|
|
des.replace_terrain({ mapfragment = [[w]], toterrain = "L", chance = 20 });
|
|
des.replace_terrain({ mapfragment = [[w]], toterrain = ".", chance = 15 });
|
|
end,
|
|
|
|
-- 2: mazes like original, with some hell_tweaks
|
|
function ()
|
|
des.level_init({ style = "solidfill", fg = " ", lit = 0 });
|
|
des.level_flags("mazelevel", "noflip");
|
|
des.level_init({ style = "mazegrid", bg = "-" });
|
|
des.mazewalk({ coord = {01,10}, dir = "east", stocked = false});
|
|
local tmpbounds = selection.match("-");
|
|
local bnds = tmpbounds:bounds();
|
|
local protected_area = selection.fillrect(bnds.lx, bnds.ly + 1, bnds.hx - 2, bnds.hy - 1);
|
|
hell_tweaks(protected_area:negate());
|
|
if (percent(25)) then
|
|
rnd_hell_prefab(false);
|
|
end
|
|
end,
|
|
|
|
-- 3: mazes, style 1: wall thick = 1, random wid corr
|
|
function ()
|
|
des.level_init({ style = "solidfill", fg = " ", lit = 0 });
|
|
des.level_flags("mazelevel", "noflip");
|
|
des.level_init({ style = "maze", wallthick = 1 });
|
|
end,
|
|
|
|
-- 4: mazes, style 2: replace wall with iron bars or lava
|
|
function ()
|
|
local cwid = math.random(4);
|
|
des.level_init({ style = "solidfill", fg = " ", lit = 0 });
|
|
des.level_flags("mazelevel", "noflip");
|
|
des.level_init({ style = "maze", wallthick = 1, corrwid = cwid });
|
|
local outside_walls = selection.match(" ");
|
|
local wallterrain = { "F", "L" };
|
|
shuffle(wallterrain);
|
|
des.replace_terrain({ mapfragment = "w", toterrain = wallterrain[1] });
|
|
if (cwid == 1) then
|
|
if (wallterrain[1] == "F" and percent(80)) then
|
|
-- replace some horizontal iron bars walls with floor
|
|
des.replace_terrain({ mapfragment = ".\nF\n.", toterrain = ".", chance = 25 * math.random(4) });
|
|
elseif (percent(25)) then
|
|
rnd_hell_prefab(false);
|
|
end
|
|
end
|
|
des.terrain(outside_walls, " "); -- return the outside back to solid wall
|
|
end,
|
|
|
|
-- 5: mazes, thick walls, occasionally lava instead of walls
|
|
function ()
|
|
des.level_init({ style = "solidfill", fg = " ", lit = 0 });
|
|
des.level_flags("mazelevel", "noflip");
|
|
des.level_init({ style = "maze", wallthick = 1 + math.random(2), corrwid = math.random(2) });
|
|
if (percent(50)) then
|
|
local outside_walls = selection.match(" ");
|
|
des.replace_terrain({ mapfragment = "w", toterrain = "L" });
|
|
des.terrain(outside_walls, " "); -- return the outside back to solid wall
|
|
end
|
|
end,
|
|
|
|
-- 6: cold maze, with ice and water
|
|
function ()
|
|
local cwid = math.random(4);
|
|
des.level_init({ style = "solidfill", fg = " ", lit = 0 });
|
|
des.level_flags("mazelevel", "noflip", "cold");
|
|
des.level_init({ style = "maze", wallthick = 1, corrwid = cwid });
|
|
local outside_walls = selection.match(" ");
|
|
local icey = selection.negate():percentage(10):grow():filter_mapchar(".");
|
|
des.terrain(icey, "I");
|
|
if (cwid > 1) then
|
|
-- turn some ice into wall of water
|
|
des.terrain(icey:percentage(1), "W");
|
|
end
|
|
des.terrain(icey:percentage(5), "P");
|
|
if (percent(25)) then
|
|
des.terrain(selection.match("w"), "W"); -- walls of water
|
|
end
|
|
if (cwid == 1 and percent(25)) then
|
|
rnd_hell_prefab(true);
|
|
end
|
|
des.terrain(outside_walls, " "); -- return the outside back to solid wall
|
|
end,
|
|
};
|
|
|
|
local hellno = math.random(1, #hells);
|
|
hells[hellno]();
|
|
|
|
--
|
|
|
|
des.stair("up")
|
|
if (u.invocation_level) then
|
|
des.trap("vibrating square");
|
|
else
|
|
des.stair("down")
|
|
end
|
|
|
|
populatemaze();
|