diff --git a/dat/themerms.lua b/dat/themerms.lua index 454059125..f42111dc7 100644 --- a/dat/themerms.lua +++ b/dat/themerms.lua @@ -1,4 +1,4 @@ --- NetHack themerms.lua $NHDT-Date: 1652196294 2022/05/10 15:24:54 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.16 $ +-- NetHack themerms.lua $NHDT-Date: 1743399789 2025/03/30 21:43:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.36 $ -- Copyright (c) 2020 by Pasi Kallinen -- NetHack may be freely redistributed. See license for details. -- @@ -81,8 +81,7 @@ themeroom_fills = { -- Trap room function(rm) local traps = { "arrow", "dart", "falling rock", "bear", - "land mine", "sleep gas", "rust", - "anti magic" }; + "land mine", "sleep gas", "rust", "anti magic" }; shuffle(traps); local locs = selection.room():percentage(30); local func = function(x,y) @@ -103,22 +102,28 @@ themeroom_fills = { des.feature("fountain"); end end - table.insert(postprocess, { handler = make_garden_walls, data = { sel = selection.room() } }); + table.insert(postprocess, { handler = make_garden_walls, + data = { sel = selection.room() } }); end }, -- Buried treasure function(rm) - des.object({ id = "chest", buried = true, contents = function(otmp) - local xobj = otmp:totable(); - -- keep track of the last buried treasure - if (xobj.NO_OBJ == nil) then - table.insert(postprocess, { handler = make_dig_engraving, data = { x = xobj.ox, y = xobj.oy }}); - end - for i = 1, d(3,4) do - des.object(); - end - end }); + des.object({ + id = "chest", buried = true, + contents = function(otmp) + local xobj = otmp:totable(); + -- keep track of the last buried treasure + if (xobj.NO_OBJ == nil) then + table.insert(postprocess, { handler = make_dig_engraving, + data = { x = xobj.ox, + y = xobj.oy } }); + end + for i = 1, d(3, 4) do + des.object(); + end + end + }); end, -- Buried zombies @@ -229,7 +234,9 @@ themeroom_fills = { if (pos.x > 0) then pos.x = pos.x + rm.region.x1 - 1; pos.y = pos.y + rm.region.y1; - table.insert(postprocess, { handler = make_a_trap, data = { type = "teleport", seen = true, coord = pos, teledest = 1 } }); + table.insert(postprocess, { handler = make_a_trap, + data = { type = "teleport", seen = true, + coord = pos, teledest = 1 } }); end end end, @@ -248,7 +255,8 @@ themerooms = { function() des.room({ type = "ordinary", w = 11,h = 9, filled = 1, contents = function() - des.room({ type = "ordinary", x = 4,y = 3, w = 3,h = 3, filled = 1, + des.room({ type = "ordinary", x = 4, y = 3, w = 3, h = 3, + filled = 1, contents = function() des.door({ state="random", wall="all" }); end @@ -272,7 +280,8 @@ themerooms = { -- Huge room, with another room inside (90%) function() - des.room({ type = "ordinary", w = nh.rn2(10)+11,h = nh.rn2(5)+8, filled = 1, + des.room({ type = "ordinary", w = nh.rn2(10) + 11, h = nh.rn2(5) + 8, + filled = 1, contents = function() if (percent(90)) then des.room({ type = "ordinary", filled = 1, @@ -360,15 +369,17 @@ themerooms = { des.room({ type = "themed", w = 5 + nh.rn2(3)*2, h = 5 + nh.rn2(3)*2, contents = function(rm) des.room({ type = "themed", - x = (rm.width - 1) / 2, y = (rm.height - 1) / 2, - w = 1, h = 1, joined = false, + x = (rm.width - 1) / 2, y = (rm.height - 1) / 2, + w = 1, h = 1, joined = false, contents = function() if (percent(50)) then local mons = { "M", "V", "L", "Z" }; shuffle(mons); - des.monster({ class = mons[1], x=0,y=0, waiting = 1 }); + des.monster({ class = mons[1], x=0, y=0, + waiting = 1 }); else - des.object({ id = "corpse", montype = "@", coord = {0,0} }); + des.object({ id = "corpse", montype = "@", + coord = {0,0} }); end if (percent(20)) then des.door({ state="secret", wall="all" }); @@ -388,7 +399,7 @@ themerooms = { local feature = { "C", "L", "I", "P", "T" }; shuffle(feature); des.terrain((rm.width - 1) / 2, (rm.height - 1) / 2, - feature[1]); + feature[1]); end }); end, @@ -459,13 +470,15 @@ xxx-----]], contents = function(m) filler_region(1,1); end }); |.........| |.........| -----------]], contents = function(m) -if (percent(30)) then - local terr = { "-", "P" }; - shuffle(terr); - des.replace_terrain({ region = {1,1, 9,9}, fromterrain = "L", toterrain = terr[1] }); -end -filler_region(1,1); -end }); + if (percent(30)) then + local terr = { "-", "P" }; + shuffle(terr); + des.replace_terrain({ region = {1,1, 9,9}, + fromterrain = "L", toterrain = terr[1] }); + end + filler_region(1,1); + end + }); end, -- Circular, small @@ -666,102 +679,116 @@ xx|.....|xx }|..|} }|..|} }----} -}}}}}}]], contents = function(m) des.region({ region={3,3,3,3}, type="themed", irregular=true, filled=0, joined=false }); - local nasty_undead = { "giant zombie", "ettin zombie", "vampire lord" }; - local chest_spots = { { 2, 2 }, { 3, 2 }, { 2, 3 }, { 3, 3 } }; +}}}}}}]], contents = function(m) des.region({ region = {3, 3, 3, 3}, + type = "themed", irregular = true, + filled = 0, joined = false }); + local nasty_undead = { "giant zombie", "ettin zombie", + "vampire lord" }; + local chest_spots = { { 2, 2 }, { 3, 2 }, { 2, 3 }, { 3, 3 } }; - shuffle(chest_spots) - -- Guarantee an escape item inside one of the chests, to prevent the hero - -- falling in from above and becoming permanently stuck - -- [cf. generate_way_out_method(sp_lev.c)]. - -- If the escape item is made of glass or crystal, make sure that the - -- chest isn't locked so that kicking it to gain access to its contents - -- won't be necessary; otherwise retain lock state from random creation. - -- "pick-axe", "dwarvish mattock" could be included in the list of escape - -- items but don't normally generate in containers. - local escape_items = { - "scroll of teleportation", "ring of teleportation", - "wand of teleportation", "wand of digging" - }; - local itm = obj.new(escape_items[math.random(#escape_items)]); - local itmcls = itm:class() - local box - if itmcls[ "material" ] == "glass" then - -- explicitly force chest to be unlocked - box = des.object({ id = "chest", coord = chest_spots[1], - olocked = "no" }); - else - -- accept random locked/unlocked state - box = des.object({ id = "chest", coord = chest_spots[1] }); - end; - box:addcontent(itm); + shuffle(chest_spots) + -- Guarantee an escape item inside one of the chests, to prevent the + -- hero falling in from above and becoming permanently stuck + -- [cf. generate_way_out_method(sp_lev.c)]. + -- If the escape item is made of glass or crystal, make sure that + -- the chest isn't locked so that kicking it to gain access to its + -- contents won't be necessary; otherwise retain lock state from + -- random creation. "pick-axe", "dwarvish mattock" could be included + -- in list of escape items but don't normally generate in containers. + local escape_items = { + "scroll of teleportation", "ring of teleportation", + "wand of teleportation", "wand of digging" + }; + local itm = obj.new( escape_items[math.random(#escape_items)] ); + local itmcls = itm:class() + local box + if itmcls[ "material" ] == "glass" then + -- explicitly force chest to be unlocked + box = des.object({ id = "chest", coord = chest_spots[1], + olocked = "no" }); + else + -- accept random locked/unlocked state + box = des.object({ id = "chest", coord = chest_spots[1] }); + end; + box:addcontent(itm); - for i = 2, #chest_spots do - des.object({ id = "chest", coord = chest_spots[i] }); - end + for i = 2, #chest_spots do + des.object({ id = "chest", coord = chest_spots[i] }); + end - shuffle(nasty_undead); - des.monster(nasty_undead[1], 2, 2); - des.exclusion({ type = "teleport", region = { 2,2, 3,3 } }); -end }); + shuffle(nasty_undead); + des.monster(nasty_undead[1], 2, 2); + des.exclusion({ type = "teleport", region = { 2,2, 3,3 } }); + end + }); -- des.map end, -- Twin businesses { - mindiff = 4; -- arbitrary + mindiff = 4; -- arbitrary; needs to be greater than 1 since no shops on 1 contents = function() -- Due to the way room connections work in mklev.c, we must guarantee -- that the "aisle" between the shops touches all four walls of the -- larger room. Thus it has an extra width and height. des.room({ type="themed", w=9, h=5, contents = function() - -- There are eight possible placements of the two shops, four of - -- which have the vertical aisle in the center. - southeast = function() return percent(50) and "south" or "east" end - northeast = function() return percent(50) and "north" or "east" end - northwest = function() return percent(50) and "north" or "west" end - southwest = function() return percent(50) and "south" or "west" end - placements = { - { lx = 1, ly = 1, rx = 4, ry = 1, lwall = "south", rwall = southeast() }, - { lx = 1, ly = 2, rx = 4, ry = 2, lwall = "north", rwall = northeast() }, - { lx = 1, ly = 1, rx = 5, ry = 1, lwall = southeast(), rwall = southwest() }, - { lx = 1, ly = 1, rx = 5, ry = 2, lwall = southeast(), rwall = northwest() }, - { lx = 1, ly = 2, rx = 5, ry = 1, lwall = northeast(), rwall = southwest() }, - { lx = 1, ly = 2, rx = 5, ry = 2, lwall = northeast(), rwall = northwest() }, - { lx = 2, ly = 1, rx = 5, ry = 1, lwall = southwest(), rwall = "south" }, - { lx = 2, ly = 2, rx = 5, ry = 2, lwall = northwest(), rwall = "north" } - } - ltype,rtype = "weapon shop","armor shop" - if percent(50) then - ltype,rtype = rtype,ltype + -- There are eight possible placements of the two shops, four of + -- which have the vertical aisle in the center. + southeast = function() return percent(50) and "south" or "east" end + northeast = function() return percent(50) and "north" or "east" end + northwest = function() return percent(50) and "north" or "west" end + southwest = function() return percent(50) and "south" or "west" end + placements = { + { lx = 1, ly = 1, rx = 4, ry = 1, + lwall = "south", rwall = southeast() }, + { lx = 1, ly = 2, rx = 4, ry = 2, + lwall = "north", rwall = northeast() }, + { lx = 1, ly = 1, rx = 5, ry = 1, + lwall = southeast(), rwall = southwest() }, + { lx = 1, ly = 1, rx = 5, ry = 2, + lwall = southeast(), rwall = northwest() }, + { lx = 1, ly = 2, rx = 5, ry = 1, + lwall = northeast(), rwall = southwest() }, + { lx = 1, ly = 2, rx = 5, ry = 2, + lwall = northeast(), rwall = northwest() }, + { lx = 2, ly = 1, rx = 5, ry = 1, + lwall = southwest(), rwall = "south" }, + { lx = 2, ly = 2, rx = 5, ry = 2, + lwall = northwest(), rwall = "north" } + } + ltype, rtype = "weapon shop", "armor shop" + if percent(50) then + ltype, rtype = rtype, ltype + end + shopdoorstate = function() + if percent(1) then + return "locked" + elseif percent(50) then + return "closed" + else + return "open" end - shopdoorstate = function() - if percent(1) then - return "locked" - elseif percent(50) then - return "closed" - else - return "open" - end - end - p = placements[d(#placements)] - des.room({ type=ltype, x=p["lx"], y=p["ly"], w=3, h=3, filled=1, joined=false, - contents = function() - des.door({ state=shopdoorstate(), wall=p["lwall"] }) - end - }); - des.room({ type=rtype, x=p["rx"], y=p["ry"], w=3, h=3, filled=1, joined=false, - contents = function() - des.door({ state=shopdoorstate(), wall=p["rwall"] }) - end - }); + end + p = placements[ d(#placements) ] + des.room({ type = ltype, x = p["lx"], y = p["ly"], w = 3, h = 3, + filled = 1, joined = false, + contents = function() + des.door({ state = shopdoorstate(), + wall = p["lwall"] }) + end + }); + des.room({ type = rtype, x = p["rx"], y = p["ry"], w = 3, h = 3, + filled = 1, joined = false, + contents = function() + des.door({ state = shopdoorstate(), + wall = p["rwall"] }) + end + }); end }); end }, - }; - function filler_region(x, y) local rmtyp = "ordinary"; local func = nil; @@ -769,7 +796,8 @@ function filler_region(x, y) rmtyp = "themed"; func = themeroom_fill; end - des.region({ region={x,y,x,y}, type=rmtyp, irregular=true, filled=1, contents = func }); + des.region({ region = {x,y,x,y}, type = rmtyp, irregular = true, filled = 1, + contents = func }); end function is_eligible(room, mkrm) @@ -799,14 +827,16 @@ function themerooms_generate() -- which may change on different levels because of level difficulty. if is_eligible(themerooms[i], nil) then local this_frequency; - if (type(themerooms[i]) == "table" and themerooms[i].frequency ~= nil) then + if (type(themerooms[i]) == "table" and themerooms[i].frequency ~= nil) + then this_frequency = themerooms[i].frequency; else this_frequency = 1; end total_frequency = total_frequency + this_frequency; -- avoid rn2(0) if a room has freq 0 - if this_frequency > 0 and nh.rn2(total_frequency) < this_frequency then + if this_frequency > 0 and nh.rn2(total_frequency) < this_frequency + then pick = i; end end @@ -837,14 +867,16 @@ function themeroom_fill(rm) -- which may change on different levels because of level difficulty. if is_eligible(themeroom_fills[i], rm) then local this_frequency; - if (type(themeroom_fills[i]) == "table" and themeroom_fills[i].frequency ~= nil) then + if (type(themeroom_fills[i]) == "table" + and themeroom_fills[i].frequency ~= nil) then this_frequency = themeroom_fills[i].frequency; else this_frequency = 1; end total_frequency = total_frequency + this_frequency; -- avoid rn2(0) if a room has freq 0 - if this_frequency > 0 and nh.rn2(total_frequency) < this_frequency then + if this_frequency > 0 and nh.rn2(total_frequency) < this_frequency + then pick = i; end end @@ -869,10 +901,12 @@ function make_dig_engraving(data) dig = " here"; else if (tx < 0 or tx > 0) then - dig = string.format(" %i %s", math.abs(tx), (tx > 0) and "east" or "west"); + dig = string.format(" %i %s", math.abs(tx), + (tx > 0) and "east" or "west"); end if (ty < 0 or ty > 0) then - dig = dig .. string.format(" %i %s", math.abs(ty), (ty > 0) and "south" or "north"); + dig = dig .. string.format(" %i %s", math.abs(ty), + (ty > 0) and "south" or "north"); end end des.engraving({ coord = pos, type = "burn", text = "Dig" .. dig }); @@ -890,7 +924,8 @@ function make_a_trap(data) local locs = selection.negate():filter_mapchar("."); repeat data.teledest = locs:rndcoord(1); - until (data.teledest.x ~= data.coord.x and data.teledest.y ~= data.coord.y); + until (data.teledest.x ~= data.coord.x + and data.teledest.y ~= data.coord.y); end des.trap(data); end