More interesting Gehennom levels

Instead of just plain old boring mazes, spice up Gehennom by
occasionally adding lava, iron bars, or even mines-style levels
(with lava, of course).

Of the fixed Gehennom levels, only Asmodeus' lair has been changed
to add some random lava pools.

Also some lua fixes and changes:
- Fixed a selection negation bounding box being wrong.
- Fixed a selection negated and ORed returning wrong results.
- des.map now returns a selection of the map grids it touched.
- When using des.map contents-function the commands following the
  map are not relative to it.
This commit is contained in:
Pasi Kallinen
2023-01-09 22:25:23 +02:00
parent 7c72c1f141
commit 4af086be73
13 changed files with 310 additions and 65 deletions

View File

@@ -6,8 +6,13 @@
des.level_init({ style="mazegrid", bg ="-" }); des.level_init({ style="mazegrid", bg ="-" });
des.level_flags("mazelevel") des.level_flags("mazelevel")
local tmpbounds = selection.match("-");
local bnds = tmpbounds:bounds();
local bounds2 = selection.fillrect(bnds.lx, bnds.ly + 1, bnds.hx - 2, bnds.hy - 1);
-- First part -- First part
des.map({ halign = "half-left", valign = "center", map = [[ local asmo1 = des.map({ halign = "half-left", valign = "center", map = [[
--------------------- ---------------------
|.............|.....| |.............|.....|
|.............S.....| |.............S.....|
@@ -20,15 +25,7 @@ des.map({ halign = "half-left", valign = "center", map = [[
|..|..-----------...| |..|..-----------...|
|..S..........|.....| |..S..........|.....|
--------------------- ---------------------
]] }); ]], contents = function(rm)
des.levregion({ region={01,00,6,20}, region_islev=1, exclude={6,1,70,16}, exclude_islev=1, type="stair-up" });
-- des.stair(levregion(01,00,6,20),levregion(6,1,70,16),up)
des.levregion({ region={01,00,6,20}, region_islev=1, exclude={6,1,70,16}, exclude_islev=1, type="branch" });
-- des.branch(levregion(01,00,6,20),levregion(6,1,70,16))
des.teleport_region({ region = {01,00,6,20}, region_islev=1, exclude={6,1,70,16}, exclude_islev=1 })
-- Doors -- Doors
des.door("closed",04,03) des.door("closed",04,03)
des.door("locked",18,04) des.door("locked",18,04)
@@ -68,14 +65,21 @@ des.monster("L")
des.monster("V") des.monster("V")
des.monster("V") des.monster("V")
des.monster("V") des.monster("V")
end });
des.levregion({ region={01,00,6,20}, region_islev=1, exclude={6,1,70,16}, exclude_islev=1, type="stair-up" });
des.levregion({ region={01,00,6,20}, region_islev=1, exclude={6,1,70,16}, exclude_islev=1, type="branch" });
des.teleport_region({ region = {01,00,6,20}, region_islev=1, exclude={6,1,70,16}, exclude_islev=1 })
-- Second part -- Second part
des.map({ halign = "half-right", valign = "center", map = [[ local asmo2 = des.map({ halign = "half-right", valign = "center", map = [[
--------------------------------- ---------------------------------
................................| ................................|
................................+ ................................+
................................| ................................|
--------------------------------- ---------------------------------
]] }); ]], contents = function(rm)
des.mazewalk(32,02,"east") des.mazewalk(32,02,"east")
-- Non diggable walls -- Non diggable walls
des.non_diggable(selection.area(00,00,32,04)) des.non_diggable(selection.area(00,00,32,04))
@@ -86,3 +90,7 @@ des.monster("&")
des.trap("anti magic") des.trap("anti magic")
des.trap("fire") des.trap("fire")
des.trap("magic") des.trap("magic")
end });
local protected = bounds2:negate() | asmo1 | asmo2;
hell_tweaks(protected);

View File

@@ -91,6 +91,7 @@ dungeon = {
base = 20, base = 20,
range = 5, range = 5,
flags = { "mazelike", "hellish" }, flags = { "mazelike", "hellish" },
lvlfill = "hellfill",
alignment = "noalign", alignment = "noalign",
branches = { branches = {
{ {

155
dat/hellfill.lua Normal file
View File

@@ -0,0 +1,155 @@
-- 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
--
-- TODO: cold hells? (ice & water instead of lava. sometimes floor = ice)
-- TODO: more hell_tweaks:
-- - replacing walls with iron bars
-- - random prefab areas
-- - replace all walls (in a full level width/height) in a small area
hells = {
-- 1: "mines" style with lava
function ()
des.level_init({ style = "solidfill", fg = " " });
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_flags("mazelevel", "noflip");
des.level_init({ style = "mazegrid", bg = "-" });
des.mazewalk(01,10,"east");
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());
end,
-- 3: mazes, style 1: wall thick = 1, random wid corr
function ()
des.level_init({ style = "solidfill", fg = " " });
des.level_flags("mazelevel", "noflip");
des.level_init({ style = "maze", wallthick = 1 });
end,
-- 4: mazes, style 2: replace wall with iron bars of lava
function ()
des.level_init({ style = "solidfill", fg = " " });
des.level_flags("mazelevel", "noflip");
des.level_init({ style = "maze", wallthick = 1 });
local outside_walls = selection.match(" ");
local wallterrain = { "F", "L" };
shuffle(wallterrain);
des.replace_terrain({ mapfragment = "w", toterrain = wallterrain[1] });
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 = " " });
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,
};
local hellno = math.random(1, #hells);
hells[hellno]();
--
des.stair("up")
des.stair("down")
des.region(selection.area(00,00,77,21),"unlit");
populatemaze();

View File

@@ -51,6 +51,58 @@ function monkfoodshop()
return "food shop"; return "food shop";
end 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 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 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
if (percent(25)) then
local riverbanks = selection.grow(lavariver);
riverbanks = riverbanks & prot;
des.terrain(selection.percentage(riverbanks, 50), ground);
end
lavariver = lavariver & prot;
des.terrain(lavariver, liquid);
end
end
-- pline with variable number of arguments -- pline with variable number of arguments
function pline(fmt, ...) function pline(fmt, ...)
nh.pline(string.format(fmt, table.unpack({...}))); nh.pline(string.format(fmt, table.unpack({...})));

View File

@@ -1081,6 +1081,7 @@ prevent paralyzed hero from helping nymph or succubus remove worn armor
when identifying items via menu and more than one pass is needed (so when when identifying items via menu and more than one pass is needed (so when
identifying 3 items and player only picks 1, for instance), issue identifying 3 items and player only picks 1, for instance), issue
--More-- because the next menu might cover up the ID message(s) --More-- because the next menu might cover up the ID message(s)
slightly more interesting Gehennom filler levels
Fixes to 3.7.0-x Problems that Were Exposed Via git Repository Fixes to 3.7.0-x Problems that Were Exposed Via git Repository

View File

@@ -544,7 +544,9 @@ Example:
=== map === map
Construct a piece of the level from text map. Takes one parameter, either a text string Construct a piece of the level from text map. Takes one parameter, either a text string
describing the map, or a table with multiple parameters. describing the map, or a table with multiple parameters. Returns a <<_selection>> where
the map locations were put down on. If a contents-function is used, the commands following
the map are not relative to it.
[options="header"] [options="header"]
|=== |===
@@ -570,6 +572,7 @@ Example:
des.terrain(0,0, "L"); des.terrain(0,0, "L");
des.terrain(map.width-1, map.height-1, "T"); des.terrain(map.width-1, map.height-1, "T");
end }); end });
local sel = des.map([[LLL]]);
=== mazewalk === mazewalk

View File

@@ -1815,6 +1815,7 @@ extern int do_play_instrument(struct obj *);
#if !defined(CROSSCOMPILE) || defined(CROSSCOMPILE_TARGET) #if !defined(CROSSCOMPILE) || defined(CROSSCOMPILE_TARGET)
extern struct selectionvar *l_selection_check(lua_State *, int); extern struct selectionvar *l_selection_check(lua_State *, int);
extern int l_selection_register(lua_State *); extern int l_selection_register(lua_State *);
extern void l_selection_push_copy(lua_State *, struct selectionvar *);
extern void nhl_push_obj(lua_State *, struct obj *); extern void nhl_push_obj(lua_State *, struct obj *);
extern int nhl_obj_u_giveobj(lua_State *); extern int nhl_obj_u_giveobj(lua_State *);
extern int l_obj_register(lua_State *); extern int l_obj_register(lua_State *);

View File

@@ -8,7 +8,6 @@
struct selectionvar *l_selection_check(lua_State *, int); struct selectionvar *l_selection_check(lua_State *, int);
static struct selectionvar *l_selection_push_new(lua_State *); static struct selectionvar *l_selection_push_new(lua_State *);
static void l_selection_push_copy(lua_State *, struct selectionvar *);
/* lua_CFunction prototypes */ /* lua_CFunction prototypes */
static int l_selection_new(lua_State *); static int l_selection_new(lua_State *);
@@ -106,7 +105,7 @@ l_selection_push_new(lua_State *L)
} }
/* push a copy of selectionvar tmp to lua stack */ /* push a copy of selectionvar tmp to lua stack */
static void void
l_selection_push_copy(lua_State *L, struct selectionvar *tmp) l_selection_push_copy(lua_State *L, struct selectionvar *tmp)
{ {
struct selectionvar struct selectionvar
@@ -247,6 +246,7 @@ l_selection_not(lua_State *L)
(void) l_selection_clone(L); (void) l_selection_clone(L);
sel2 = l_selection_check(L, 2); sel2 = l_selection_check(L, 2);
selection_not(sel2); selection_not(sel2);
lua_remove(L, 1);
} }
return 1; return 1;
} }

View File

@@ -4600,12 +4600,12 @@ struct selectionvar *
selection_not(struct selectionvar* s) selection_not(struct selectionvar* s)
{ {
int x, y; int x, y;
NhRect tmprect;
for (x = 0; x < s->wid; x++) for (x = 0; x < s->wid; x++)
for (y = 0; y < s->hei; y++) for (y = 0; y < s->hei; y++)
selection_setpoint(x, y, s, selection_getpoint(x, y, s) ? 0 : 1); selection_setpoint(x, y, s, selection_getpoint(x, y, s) ? 0 : 1);
selection_getbounds(s, &tmprect);
return s; return s;
} }
@@ -6466,6 +6466,7 @@ DISABLE_WARNING_UNREACHABLE_CODE
/* map({ halign = "center", valign = "center", map = [[...]] }); */ /* map({ halign = "center", valign = "center", map = [[...]] }); */
/* map({ map = [[...]], contents = function(map) ... end }); */ /* map({ map = [[...]], contents = function(map) ... end }); */
/* map([[...]]) */ /* map([[...]]) */
/* local selection = map( ... ); */
int int
lspo_map(lua_State *L) lspo_map(lua_State *L)
{ {
@@ -6493,6 +6494,7 @@ TODO: gc.coder->croom needs to be updated
boolean has_contents = FALSE; boolean has_contents = FALSE;
int tryct = 0; int tryct = 0;
int ox, oy; int ox, oy;
struct selectionvar *sel;
create_des_coder(); create_des_coder();
@@ -6527,6 +6529,7 @@ TODO: gc.coder->croom needs to be updated
return 0; return 0;
} }
sel = selection_new();
ox = x; ox = x;
oy = y; oy = y;
redo_maploc: redo_maploc:
@@ -6575,6 +6578,7 @@ TODO: gc.coder->croom needs to be updated
} else { } else {
mapfrag_free(&mf); mapfrag_free(&mf);
nhl_error(L, "Map requires either x,y or halign,valign params"); nhl_error(L, "Map requires either x,y or halign,valign params");
selection_free(sel, TRUE);
return 0; return 0;
} }
} else { } else {
@@ -6671,6 +6675,7 @@ TODO: gc.coder->croom needs to be updated
} }
if (mptyp >= MAX_TYPE) if (mptyp >= MAX_TYPE)
continue; continue;
selection_setpoint(x, y, sel, 1);
levl[x][y].typ = mptyp; levl[x][y].typ = mptyp;
levl[x][y].lit = FALSE; levl[x][y].lit = FALSE;
/* clear out levl: load_common_data may set them */ /* clear out levl: load_common_data may set them */
@@ -6712,9 +6717,14 @@ TODO: gc.coder->croom needs to be updated
if (nhl_pcall(L, 1, 0)){ if (nhl_pcall(L, 1, 0)){
impossible("Lua error: %s", lua_tostring(L, -1)); impossible("Lua error: %s", lua_tostring(L, -1));
} }
reset_xystart_size();
} }
return 0; /* return selection where map locations were put */
l_selection_push_copy(L, sel);
selection_free(sel, TRUE);
return 1;
} }
RESTORE_WARNING_UNREACHABLE_CODE RESTORE_WARNING_UNREACHABLE_CODE

View File

@@ -97,7 +97,7 @@ SPEC_LEVS = asmodeus.lua baalz.lua bigrm-*.lua castle.lua fakewiz?.lua \
juiblex.lua knox.lua medusa-?.lua minend-?.lua minefill.lua \ juiblex.lua knox.lua medusa-?.lua minend-?.lua minefill.lua \
minetn-?.lua oracle.lua orcus.lua sanctum.lua soko?-?.lua \ minetn-?.lua oracle.lua orcus.lua sanctum.lua soko?-?.lua \
tower?.lua valley.lua wizard?.lua nhcore.lua nhlib.lua themerms.lua \ tower?.lua valley.lua wizard?.lua nhcore.lua nhlib.lua themerms.lua \
astral.lua air.lua earth.lua fire.lua water.lua astral.lua air.lua earth.lua fire.lua water.lua hellfill.lua
QUEST_LEVS = ???-goal.lua ???-fil?.lua ???-loca.lua ???-strt.lua QUEST_LEVS = ???-goal.lua ???-fil?.lua ???-loca.lua ???-strt.lua
DATNODLB = $(VARDATND) license symbols DATNODLB = $(VARDATND) license symbols

View File

@@ -1285,6 +1285,7 @@
"$(NH_DAT_DIR)/Hea-goal.lua", "$(NH_DAT_DIR)/Hea-goal.lua",
"$(NH_DAT_DIR)/Hea-loca.lua", "$(NH_DAT_DIR)/Hea-loca.lua",
"$(NH_DAT_DIR)/Hea-strt.lua", "$(NH_DAT_DIR)/Hea-strt.lua",
"$(NH_DAT_DIR)/hellfill.lua",
"$(NH_DAT_DIR)/juiblex.lua", "$(NH_DAT_DIR)/juiblex.lua",
"$(NH_DAT_DIR)/Kni-fila.lua", "$(NH_DAT_DIR)/Kni-fila.lua",
"$(NH_DAT_DIR)/Kni-filb.lua", "$(NH_DAT_DIR)/Kni-filb.lua",
@@ -1379,7 +1380,7 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "cd \"${NH_DAT_DIR}\"\n\"${NH_UTIL_DIR}\"/dlb cf nhdat help hh cmdhelp keyhelp history opthelp optmenu wizhelp dungeon.lua tribute asmodeus.lua baalz.lua bigrm-*.lua castle.lua fakewiz?.lua juiblex.lua knox.lua medusa-?.lua minend-?.lua minefill.lua minetn-?.lua oracle.lua orcus.lua sanctum.lua soko?-?.lua tower?.lua valley.lua wizard?.lua nhcore.lua nhlib.lua themerms.lua astral.lua air.lua earth.lua fire.lua water.lua ???-goal.lua ???-fil?.lua ???-loca.lua ???-strt.lua bogusmon data engrave epitaph oracles options quest.lua rumors\n"; shellScript = "cd \"${NH_DAT_DIR}\"\n\"${NH_UTIL_DIR}\"/dlb cf nhdat help hh cmdhelp keyhelp history opthelp optmenu wizhelp dungeon.lua tribute asmodeus.lua baalz.lua bigrm-*.lua castle.lua fakewiz?.lua juiblex.lua knox.lua medusa-?.lua minend-?.lua minefill.lua minetn-?.lua oracle.lua orcus.lua sanctum.lua soko?-?.lua tower?.lua valley.lua wizard?.lua nhcore.lua nhlib.lua themerms.lua hellfill.lua astral.lua air.lua earth.lua fire.lua water.lua ???-goal.lua ???-fil?.lua ???-loca.lua ???-strt.lua bogusmon data engrave epitaph oracles options quest.lua rumors\n";
}; };
3192867021A39F6A00325BEB /* Install */ = { 3192867021A39F6A00325BEB /* Install */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;

View File

@@ -243,9 +243,9 @@ III]] })
...]], contents = function(map) ...]], contents = function(map)
des.terrain(0,0, "L"); des.terrain(0,0, "L");
des.terrain(map.width-1,map.height-1, "T"); des.terrain(map.width-1,map.height-1, "T");
end});
check_loc_name(0, 0, "lava pool"); check_loc_name(0, 0, "lava pool");
check_loc_name(2, 2, "tree"); check_loc_name(2, 2, "tree");
end});
end end
function test_feature() function test_feature()

View File

@@ -483,6 +483,18 @@ function test_sel_bounds()
end end
end end
-- test des.map returning a selection
function test_sel_map()
local __func__ = "test_sel_map";
des.reset_level();
des.level_init({ style = "solidfill", fg = " " });
local sela = des.map([[LLL]]);
sel_has_n_points(sela, 3, __func__);
local selb = selection.match("L");
sel_are_equal(sela, selb, __func__);
end
nh.debug_flags({mongen = false, hunger = false, overwrite_stairs = true }); nh.debug_flags({mongen = false, hunger = false, overwrite_stairs = true });
test_selection_params(); test_selection_params();
test_sel_negate(); test_sel_negate();
@@ -502,3 +514,4 @@ test_sel_flood();
test_sel_match(); test_sel_match();
test_sel_iterate(); test_sel_iterate();
test_sel_bounds(); test_sel_bounds();
test_sel_map();