diff --git a/dat/asmodeus.lua b/dat/asmodeus.lua index 4ca6c60c1..c860e3cbc 100644 --- a/dat/asmodeus.lua +++ b/dat/asmodeus.lua @@ -6,8 +6,13 @@ des.level_init({ style="mazegrid", bg ="-" }); 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 -des.map({ halign = "half-left", valign = "center", map = [[ +local asmo1 = des.map({ halign = "half-left", valign = "center", map = [[ --------------------- |.............|.....| |.............S.....| @@ -20,69 +25,72 @@ des.map({ halign = "half-left", valign = "center", map = [[ |..|..-----------...| |..S..........|.....| --------------------- -]] }); +]], contents = function(rm) + -- Doors + des.door("closed",04,03) + des.door("locked",18,04) + des.door("closed",18,08) + -- + des.stair("down", 13,07) + -- Non diggable walls + des.non_diggable(selection.area(00,00,20,11)) + -- Entire main area + des.region(selection.area(01,01,20,10),"unlit") + -- The fellow in residence + des.monster("Asmodeus",12,07) + -- Some random weapons and armor. + des.object("[") + des.object("[") + des.object(")") + des.object(")") + des.object("*") + des.object("!") + des.object("!") + des.object("?") + des.object("?") + des.object("?") + -- Some traps. + des.trap("spiked pit", 05,02) + des.trap("fire", 08,06) + des.trap("sleep gas") + des.trap("anti magic") + des.trap("fire") + des.trap("magic") + des.trap("magic") + -- Random monsters. + des.monster("ghost",11,07) + des.monster("horned devil",10,05) + des.monster("L") + -- Some Vampires for good measure + 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.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 -des.door("closed",04,03) -des.door("locked",18,04) -des.door("closed",18,08) --- -des.stair("down", 13,07) --- Non diggable walls -des.non_diggable(selection.area(00,00,20,11)) --- Entire main area -des.region(selection.area(01,01,20,10),"unlit") --- The fellow in residence -des.monster("Asmodeus",12,07) --- Some random weapons and armor. -des.object("[") -des.object("[") -des.object(")") -des.object(")") -des.object("*") -des.object("!") -des.object("!") -des.object("?") -des.object("?") -des.object("?") --- Some traps. -des.trap("spiked pit", 05,02) -des.trap("fire", 08,06) -des.trap("sleep gas") -des.trap("anti magic") -des.trap("fire") -des.trap("magic") -des.trap("magic") --- Random monsters. -des.monster("ghost",11,07) -des.monster("horned devil",10,05) -des.monster("L") --- Some Vampires for good measure -des.monster("V") -des.monster("V") -des.monster("V") -- Second part -des.map({ halign = "half-right", valign = "center", map = [[ +local asmo2 = des.map({ halign = "half-right", valign = "center", map = [[ --------------------------------- ................................| ................................+ ................................| --------------------------------- -]] }); -des.mazewalk(32,02,"east") --- Non diggable walls -des.non_diggable(selection.area(00,00,32,04)) -des.door("closed",32,02) -des.monster("&") -des.monster("&") -des.monster("&") -des.trap("anti magic") -des.trap("fire") -des.trap("magic") +]], contents = function(rm) + des.mazewalk(32,02,"east") + -- Non diggable walls + des.non_diggable(selection.area(00,00,32,04)) + des.door("closed",32,02) + des.monster("&") + des.monster("&") + des.monster("&") + des.trap("anti magic") + des.trap("fire") + des.trap("magic") +end }); + +local protected = bounds2:negate() | asmo1 | asmo2; +hell_tweaks(protected); diff --git a/dat/dungeon.lua b/dat/dungeon.lua index 23fc22138..c4371e5cb 100644 --- a/dat/dungeon.lua +++ b/dat/dungeon.lua @@ -91,6 +91,7 @@ dungeon = { base = 20, range = 5, flags = { "mazelike", "hellish" }, + lvlfill = "hellfill", alignment = "noalign", branches = { { diff --git a/dat/hellfill.lua b/dat/hellfill.lua new file mode 100644 index 000000000..e1325d8e3 --- /dev/null +++ b/dat/hellfill.lua @@ -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(); diff --git a/dat/nhlib.lua b/dat/nhlib.lua index 8bdd87ffc..9cb3f0a2e 100644 --- a/dat/nhlib.lua +++ b/dat/nhlib.lua @@ -51,6 +51,58 @@ function monkfoodshop() 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 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 function pline(fmt, ...) nh.pline(string.format(fmt, table.unpack({...}))); diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 1243d392e..f890f04cc 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -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 identifying 3 items and player only picks 1, for instance), issue --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 diff --git a/doc/lua.adoc b/doc/lua.adoc index e7d30618e..632ccd949 100644 --- a/doc/lua.adoc +++ b/doc/lua.adoc @@ -544,7 +544,9 @@ Example: === map 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"] |=== @@ -570,6 +572,7 @@ Example: des.terrain(0,0, "L"); des.terrain(map.width-1, map.height-1, "T"); end }); + local sel = des.map([[LLL]]); === mazewalk diff --git a/include/extern.h b/include/extern.h index f3dd64ed3..54235811e 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1815,6 +1815,7 @@ extern int do_play_instrument(struct obj *); #if !defined(CROSSCOMPILE) || defined(CROSSCOMPILE_TARGET) extern struct selectionvar *l_selection_check(lua_State *, int); 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 int nhl_obj_u_giveobj(lua_State *); extern int l_obj_register(lua_State *); diff --git a/src/nhlsel.c b/src/nhlsel.c index a634a71b5..1097b1d48 100644 --- a/src/nhlsel.c +++ b/src/nhlsel.c @@ -8,7 +8,6 @@ struct selectionvar *l_selection_check(lua_State *, int); static struct selectionvar *l_selection_push_new(lua_State *); -static void l_selection_push_copy(lua_State *, struct selectionvar *); /* lua_CFunction prototypes */ 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 */ -static void +void l_selection_push_copy(lua_State *L, struct selectionvar *tmp) { struct selectionvar @@ -247,6 +246,7 @@ l_selection_not(lua_State *L) (void) l_selection_clone(L); sel2 = l_selection_check(L, 2); selection_not(sel2); + lua_remove(L, 1); } return 1; } diff --git a/src/sp_lev.c b/src/sp_lev.c index f0bc62735..042eb1523 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -4600,12 +4600,12 @@ struct selectionvar * selection_not(struct selectionvar* s) { int x, y; - + NhRect tmprect; for (x = 0; x < s->wid; x++) for (y = 0; y < s->hei; y++) selection_setpoint(x, y, s, selection_getpoint(x, y, s) ? 0 : 1); - + selection_getbounds(s, &tmprect); return s; } @@ -6466,6 +6466,7 @@ DISABLE_WARNING_UNREACHABLE_CODE /* map({ halign = "center", valign = "center", map = [[...]] }); */ /* map({ map = [[...]], contents = function(map) ... end }); */ /* map([[...]]) */ +/* local selection = map( ... ); */ int lspo_map(lua_State *L) { @@ -6493,6 +6494,7 @@ TODO: gc.coder->croom needs to be updated boolean has_contents = FALSE; int tryct = 0; int ox, oy; + struct selectionvar *sel; create_des_coder(); @@ -6527,6 +6529,7 @@ TODO: gc.coder->croom needs to be updated return 0; } + sel = selection_new(); ox = x; oy = y; redo_maploc: @@ -6575,6 +6578,7 @@ TODO: gc.coder->croom needs to be updated } else { mapfrag_free(&mf); nhl_error(L, "Map requires either x,y or halign,valign params"); + selection_free(sel, TRUE); return 0; } } else { @@ -6671,6 +6675,7 @@ TODO: gc.coder->croom needs to be updated } if (mptyp >= MAX_TYPE) continue; + selection_setpoint(x, y, sel, 1); levl[x][y].typ = mptyp; levl[x][y].lit = FALSE; /* 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)){ 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 diff --git a/sys/unix/Makefile.top b/sys/unix/Makefile.top index f3cc25fe1..d837e9ed1 100644 --- a/sys/unix/Makefile.top +++ b/sys/unix/Makefile.top @@ -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 \ 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 + astral.lua air.lua earth.lua fire.lua water.lua hellfill.lua QUEST_LEVS = ???-goal.lua ???-fil?.lua ???-loca.lua ???-strt.lua DATNODLB = $(VARDATND) license symbols diff --git a/sys/unix/NetHack.xcodeproj/project.pbxproj b/sys/unix/NetHack.xcodeproj/project.pbxproj index 7d25c4607..f0a3ffa61 100644 --- a/sys/unix/NetHack.xcodeproj/project.pbxproj +++ b/sys/unix/NetHack.xcodeproj/project.pbxproj @@ -1285,6 +1285,7 @@ "$(NH_DAT_DIR)/Hea-goal.lua", "$(NH_DAT_DIR)/Hea-loca.lua", "$(NH_DAT_DIR)/Hea-strt.lua", + "$(NH_DAT_DIR)/hellfill.lua", "$(NH_DAT_DIR)/juiblex.lua", "$(NH_DAT_DIR)/Kni-fila.lua", "$(NH_DAT_DIR)/Kni-filb.lua", @@ -1379,7 +1380,7 @@ ); runOnlyForDeploymentPostprocessing = 0; 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 */ = { isa = PBXShellScriptBuildPhase; diff --git a/test/test_des.lua b/test/test_des.lua index afd7fcb4d..4cf7f53fd 100644 --- a/test/test_des.lua +++ b/test/test_des.lua @@ -243,9 +243,9 @@ III]] }) ...]], contents = function(map) des.terrain(0,0, "L"); des.terrain(map.width-1,map.height-1, "T"); + check_loc_name(0, 0, "lava pool"); + check_loc_name(2, 2, "tree"); end}); - check_loc_name(0, 0, "lava pool"); - check_loc_name(2, 2, "tree"); end function test_feature() diff --git a/test/test_sel.lua b/test/test_sel.lua index 72c450456..cc6d98ae9 100644 --- a/test/test_sel.lua +++ b/test/test_sel.lua @@ -483,6 +483,18 @@ function test_sel_bounds() 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 }); test_selection_params(); test_sel_negate(); @@ -502,3 +514,4 @@ test_sel_flood(); test_sel_match(); test_sel_iterate(); test_sel_bounds(); +test_sel_map();