Split themeroom shape from themeroom contents

Previously, the tetris-shaped rooms were always either
normal rooms, or turned into shops or other special rooms
in NetHack core. Now, the themed room lua code first picks
the themed room (which can be a themed or shaped), and some
of those will then pick a random filling (eg. ice floor,
traps, corpses, 3 altars).

Adds a new lua binding to create a selection picking locations
in current room.

The content-function in special level regions now get passed
the room data as a parameter.
This commit is contained in:
Pasi Kallinen
2022-09-15 18:08:32 +03:00
parent 1c177dcb39
commit 3605f18a8e
5 changed files with 270 additions and 187 deletions

View File

@@ -17,6 +17,139 @@
-- core calls themerooms_generate() multiple times per level
-- to generate a single themed room.
themeroom_fills = {
-- Ice room
function(rm)
local ice = selection.room();
des.terrain(ice, "I");
if (percent(25)) then
local mintime = 1000 - (nh.level_difficulty() * 100);
local ice_melter = function(x,y)
nh.start_timer_at(x,y, "melt-ice", mintime + nh.rn2(1000));
end;
ice:iterate(ice_melter);
end
end,
-- Boulder room
{
mindiff = 4,
contents = function(rm)
local locs = selection.room():percentage(30);
local func = function(x,y)
if (percent(50)) then
des.object("boulder", x, y);
else
des.trap("rolling boulder", x, y);
end
end;
locs:iterate(func);
end
},
-- Spider nest
function(rm)
local spooders = nh.level_difficulty() > 8;
local locs = selection.room():percentage(30);
local func = function(x,y)
des.trap({ type = "web", x = x, y = y,
spider_on_web = spooders and percent(80) });
end
locs:iterate(func);
end,
-- Trap room
function(rm)
local traps = { "arrow", "dart", "falling rock", "bear",
"land mine", "sleep gas", "rust",
"anti magic" };
shuffle(traps);
local locs = selection.room():percentage(30);
local func = function(x,y)
des.trap(traps[1], x, y);
end
locs:iterate(func);
end,
-- Buried treasure
function(rm)
des.object({ id = "chest", buried = true, contents = function()
for i = 1, d(3,4) do
des.object();
end
end });
end,
-- Massacre
function(rm)
local mon = { "apprentice", "warrior", "ninja", "thug",
"hunter", "acolyte", "abbot", "page",
"attendant", "neanderthal", "chieftain",
"student", "wizard", "valkyrie", "tourist",
"samurai", "rogue", "ranger", "priestess",
"priest", "monk", "knight", "healer",
"cavewoman", "caveman", "barbarian",
"archeologist" };
local idx = math.random(#mon);
for i = 1, d(5,5) do
if (percent(10)) then idx = math.random(#mon); end
des.object({ id = "corpse", montype = mon[idx] });
end
end,
-- Statuary
function(rm)
for i = 1, d(5,5) do
des.object({ id = "statue" });
end
for i = 1, d(3) do
des.trap("statue");
end
end,
-- Light source
{
eligible = function(rm) return rm.lit == false; end,
contents = function(rm)
des.object({ id = "oil lamp", lit = true });
end
},
-- Temple of the gods
function(rm)
des.altar({ align = align[1] });
des.altar({ align = align[2] });
des.altar({ align = align[3] });
end,
-- Ghost of an Adventurer
function(rm)
local loc = selection.room():rndcoord(0);
des.monster({ id = "ghost", asleep = true, waiting = true, coord = loc });
if percent(65) then
des.object({ id = "dagger", coord = loc, buc = "not-blessed" });
end
if percent(55) then
des.object({ class = ")", coord = loc, buc = "not-blessed" });
end
if percent(45) then
des.object({ id = "bow", coord = loc, buc = "not-blessed" });
des.object({ id = "arrow", coord = loc, buc = "not-blessed" });
end
if percent(65) then
des.object({ class = "[", coord = loc, buc = "not-blessed" });
end
if percent(20) then
des.object({ class = "=", coord = loc, buc = "not-blessed" });
end
if percent(20) then
des.object({ class = "?", coord = loc, buc = "not-blessed" });
end
end,
};
themerooms = {
{
-- the "default" room
@@ -71,114 +204,26 @@ themerooms = {
});
end,
-- Ice room
function()
des.room({ type = "themed", filled = 1,
contents = function()
local ice = selection.floodfill(1,1);
des.terrain(ice, "I");
if (percent(25)) then
local mintime = 1000 - (nh.level_difficulty() * 100);
local ice_melter = function(x,y)
nh.start_timer_at(x,y, "melt-ice", mintime + nh.rn2(1000));
end;
ice:iterate(ice_melter);
end
end
});
end,
-- Boulder room
{
mindiff = 4,
frequency = 6,
contents = function()
des.room({ type = "themed",
contents = function(rm)
for x = 0, rm.width - 1 do
for y = 0, rm.height - 1 do
if (percent(30)) then
if (percent(50)) then
des.object("boulder");
else
des.trap("rolling boulder");
end
end
end
end
end
});
des.room({ type = "themed", contents = themeroom_fill });
end
},
-- Spider nest
function()
des.room({ type = "themed",
contents = function(rm)
local spooders = nh.level_difficulty() > 8;
for x = 0, rm.width - 1 do
for y = 0, rm.height - 1 do
if (percent(30)) then
des.trap({ type = "web", x = x, y = y,
spider_on_web = spooders and percent(80) });
end
end
end
end
});
end,
{
frequency = 2,
contents = function()
des.room({ type = "themed", lit = 0, contents = themeroom_fill });
end
},
-- Trap room
function()
des.room({ type = "themed", filled = 0,
contents = function(rm)
local traps = { "arrow", "dart", "falling rock", "bear",
"land mine", "sleep gas", "rust",
"anti magic" };
shuffle(traps);
for x = 0, rm.width - 1 do
for y = 0, rm.height - 1 do
if (percent(30)) then
des.trap(traps[1], x, y);
end
end
end
end
});
end,
-- Buried treasure
function()
des.room({ type = "ordinary", filled = 1,
contents = function()
des.object({ id = "chest", buried = true, contents = function()
for i = 1, d(3,4) do
des.object();
end
end });
end
});
end,
-- Massacre
function()
des.room({ type = "themed",
contents = function()
local mon = { "apprentice", "warrior", "ninja", "thug",
"hunter", "acolyte", "abbot", "page",
"attendant", "neanderthal", "chieftain",
"student", "wizard", "valkyrie", "tourist",
"samurai", "rogue", "ranger", "priestess",
"priest", "monk", "knight", "healer",
"cavewoman", "caveman", "barbarian",
"archeologist" };
shuffle(mon);
for i = 1, d(5,5) do
if (percent(10)) then shuffle(mon); end
des.object({ id = "corpse", montype = mon[1] });
end
end
});
end,
{
frequency = 2,
contents = function()
des.room({ type = "themed", filled = 1, contents = themeroom_fill });
end
},
-- Pillars
function()
@@ -198,70 +243,6 @@ themerooms = {
});
end,
-- Statuary
function()
des.room({ type = "themed",
contents = function()
for i = 1, d(5,5) do
des.object({ id = "statue" });
end
for i = 1, d(3) do
des.trap("statue");
end
end
});
end,
-- Light source
function()
des.room({ type = "themed", lit = 0,
contents = function()
des.object({ id = "oil lamp", lit = true });
end
});
end,
-- Temple of the gods
function()
des.room({ type = "themed",
contents = function()
des.altar({ align = align[1] });
des.altar({ align = align[2] });
des.altar({ align = align[3] });
end
});
end,
-- Ghost of an Adventurer
function()
des.room({ type = "themed", lit = 0,
contents = function(rm)
local px = nh.rn2(rm.width);
local py = nh.rn2(rm.height);
des.monster({ id = "ghost", asleep = true, waiting = true, coord = {px,py} });
if percent(65) then
des.object({ id = "dagger", coord = {px,py}, buc = "not-blessed" });
end
if percent(55) then
des.object({ class = ")", coord = {px,py}, buc = "not-blessed" });
end
if percent(45) then
des.object({ id = "bow", coord = {px,py}, buc = "not-blessed" });
des.object({ id = "arrow", coord = {px,py}, buc = "not-blessed" });
end
if percent(65) then
des.object({ class = "[", coord = {px,py}, buc = "not-blessed" });
end
if percent(20) then
des.object({ class = "=", coord = {px,py}, buc = "not-blessed" });
end
if percent(20) then
des.object({ class = "?", coord = {px,py}, buc = "not-blessed" });
end
end
});
end,
-- Mausoleum
function()
des.room({ type = "themed", w = 5 + nh.rn2(3)*2, h = 5 + nh.rn2(3)*2,
@@ -310,7 +291,7 @@ themerooms = {
|......|
|......|
|......|
--------]], contents = function(m) des.region({ region={1,1,3,3}, type="ordinary", irregular=true, filled=1 }); end });
--------]], contents = function(m) filler_region(1,1); end });
end,
-- L-shaped, rot 1
@@ -323,7 +304,7 @@ xxx|...|
|......|
|......|
|......|
--------]], contents = function(m) des.region({ region={5,1,5,3}, type="ordinary", irregular=true, filled=1 }); end });
--------]], contents = function(m) filler_region(5,1); end });
end,
-- L-shaped, rot 2
@@ -336,7 +317,7 @@ xxx|...|
----...|
xxx|...|
xxx|...|
xxx-----]], contents = function(m) des.region({ region={1,1,2,2}, type="ordinary", irregular=true, filled=1 }); end });
xxx-----]], contents = function(m) filler_region(1,1); end });
end,
-- L-shaped, rot 3
@@ -349,7 +330,7 @@ xxx-----]], contents = function(m) des.region({ region={1,1,2,2}, type="ordinary
|...----
|...|xxx
|...|xxx
-----xxx]], contents = function(m) des.region({ region={1,1,2,2}, type="ordinary", irregular=true, filled=1 }); end });
-----xxx]], contents = function(m) filler_region(1,1); end });
end,
-- Blocked center
@@ -371,7 +352,7 @@ if (percent(30)) then
shuffle(terr);
des.replace_terrain({ region = {1,1, 9,9}, fromterrain = "L", toterrain = terr[1] });
end
des.region({ region={1,1,2,2}, type="ordinary", irregular=true, filled=1 });
filler_region(1,1);
end });
end,
@@ -384,7 +365,7 @@ x--.--x
|.....|
--...--
x--.--x
xx---xx]], contents = function(m) des.region({ region={3,3,3,3}, type="ordinary", irregular=true, filled=1 }); end });
xx---xx]], contents = function(m) filler_region(3,3); end });
end,
-- Circular, medium
@@ -398,7 +379,7 @@ x--...--x
|.......|
--.....--
x--...--x
xx-----xx]], contents = function(m) des.region({ region={4,4,4,4}, type="ordinary", irregular=true, filled=1 }); end });
xx-----xx]], contents = function(m) filler_region(4,4); end });
end,
-- Circular, big
@@ -414,7 +395,7 @@ x-.......-x
--.......--
x-.......-x
x---...---x
xxx-----xxx]], contents = function(m) des.region({ region={5,5,5,5}, type="ordinary", irregular=true, filled=1 }); end });
xxx-----xxx]], contents = function(m) filler_region(5,5); end });
end,
-- T-shaped
@@ -427,7 +408,7 @@ xxx|...|xxx
|.........|
|.........|
|.........|
-----------]], contents = function(m) des.region({ region={5,5,5,5}, type="ordinary", irregular=true, filled=1 }); end });
-----------]], contents = function(m) filler_region(5,5); end });
end,
-- T-shaped, rot 1
@@ -443,7 +424,7 @@ xxx|...|xxx
|...----
|...|xxx
|...|xxx
-----xxx]], contents = function(m) des.region({ region={2,2,2,2}, type="ordinary", irregular=true, filled=1 }); end });
-----xxx]], contents = function(m) filler_region(2,2); end });
end,
-- T-shaped, rot 2
@@ -456,7 +437,7 @@ xxx|...|xxx
----...----
xxx|...|xxx
xxx|...|xxx
xxx-----xxx]], contents = function(m) des.region({ region={2,2,2,2}, type="ordinary", irregular=true, filled=1 }); end });
xxx-----xxx]], contents = function(m) filler_region(2,2); end });
end,
-- T-shaped, rot 3
@@ -472,7 +453,7 @@ xxx|...|
----...|
xxx|...|
xxx|...|
xxx-----]], contents = function(m) des.region({ region={5,5,5,5}, type="ordinary", irregular=true, filled=1 }); end });
xxx-----]], contents = function(m) filler_region(5,5); end });
end,
-- S-shaped
@@ -488,7 +469,7 @@ xxx-----]], contents = function(m) des.region({ region={5,5,5,5}, type="ordinary
----...|
xxx|...|
xxx|...|
xxx-----]], contents = function(m) des.region({ region={2,2,2,2}, type="ordinary", irregular=true, filled=1 }); end });
xxx-----]], contents = function(m) filler_region(2,2); end });
end,
-- S-shaped, rot 1
@@ -501,7 +482,7 @@ xxx|......|
|......----
|......|xxx
|......|xxx
--------xxx]], contents = function(m) des.region({ region={5,5,5,5}, type="ordinary", irregular=true, filled=1 }); end });
--------xxx]], contents = function(m) filler_region(5,5); end });
end,
-- Z-shaped
@@ -517,7 +498,7 @@ xxx|...|
|...----
|...|xxx
|...|xxx
-----xxx]], contents = function(m) des.region({ region={5,5,5,5}, type="ordinary", irregular=true, filled=1 }); end });
-----xxx]], contents = function(m) filler_region(5,5); end });
end,
-- Z-shaped, rot 1
@@ -530,7 +511,7 @@ xxx|...|
----......|
xxx|......|
xxx|......|
xxx--------]], contents = function(m) des.region({ region={2,2,2,2}, type="ordinary", irregular=true, filled=1 }); end });
xxx--------]], contents = function(m) filler_region(2,2); end });
end,
-- Cross
@@ -546,7 +527,7 @@ xxx|...|xxx
----...----
xxx|...|xxx
xxx|...|xxx
xxx-----xxx]], contents = function(m) des.region({ region={6,6,6,6}, type="ordinary", irregular=true, filled=1 }); end });
xxx-----xxx]], contents = function(m) filler_region(6,6); end });
end,
-- Four-leaf clover
@@ -562,7 +543,7 @@ xx|.....|xx
|.........|
|...---...|
|...|x|...|
-----x-----]], contents = function(m) des.region({ region={6,6,6,6}, type="ordinary", irregular=true, filled=1 }); end });
-----x-----]], contents = function(m) filler_region(6,6); end });
end,
-- Water-surrounded vault
@@ -639,7 +620,18 @@ end });
};
function is_eligible(room)
function filler_region(x, y)
local rmtyp = "ordinary";
local func = nil;
if (percent(30)) then
rmtyp = "themed";
func = themeroom_fill;
end
des.region({ region={x,y,x,y}, type=rmtyp, irregular=true, filled=1, contents = func });
end
function is_eligible(room, mkrm)
local t = type(room);
local diff = nh.level_difficulty();
if (t == "table") then
@@ -648,6 +640,9 @@ function is_eligible(room)
elseif (room.maxdiff ~= nil and diff > room.maxdiff) then
return false
end
if (mkrm ~= nil and room.eligible ~= nil) then
return room.eligible(mkrm);
end
elseif (t == "function") then
-- functions currently have no constraints
end
@@ -660,7 +655,7 @@ function themerooms_generate()
for i = 1, #themerooms do
-- Reservoir sampling: select one room from the set of eligible rooms,
-- which may change on different levels because of level difficulty.
if is_eligible(themerooms[i]) then
if is_eligible(themerooms[i], nil) then
local this_frequency;
if (type(themerooms[i]) == "table" and themerooms[i].frequency ~= nil) then
this_frequency = themerooms[i].frequency;
@@ -682,3 +677,32 @@ function themerooms_generate()
themerooms[pick]();
end
end
function themeroom_fill(rm)
local pick = 1;
local total_frequency = 0;
for i = 1, #themeroom_fills do
-- Reservoir sampling: select one room from the set of eligible rooms,
-- 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
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
pick = i;
end
end
end
local t = type(themeroom_fills[pick]);
if (t == "table") then
themeroom_fills[pick].contents(rm);
elseif (t == "function") then
themeroom_fills[pick](rm);
end
end