From: copperwater Date: Wed, 13 May 2020 04:15:10 +0000 (-0400) Subject: Enable themed rooms to be constrained by level difficulty X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c5b5a869d74de7fc24a06d60cbf8842ab0d9c95a;p=nethack Enable themed rooms to be constrained by level difficulty The system of themed rooms currently makes it so that any themed room can potentially generate anywhere a themed room can be placed. This is problematic in the long run, since it makes it difficult to design new rooms that are an appropriate amount of challenge at all levels of the dungeon. (A few themed rooms already have this problem: a hero starting out on level 1 probably won't live very long when the neighboring room is full of giant spiders, or an arch-lich has generated in a mausoleum nearby). This commit adds optional "mindiff" and "maxdiff" properties for themerooms defined as tables and exposes level_difficulty() to Lua. A themeroom whose mindiff exceeds the current level difficulty, or whose maxdiff is lower than the current level difficulty, is prevented from being selected. Because the set of rooms eligible to generate on a given level is no longer fixed, the total frequency of all the rooms can't be computed once per game when the file is first parsed, as it was before. In place of this, the themerooms_generate() function now uses a reservoir sampling algorithm to choose a room from among the eligible rooms, weighted by frequency. --- diff --git a/dat/themerms.lua b/dat/themerms.lua index 85c3e18f4..db831a496 100644 --- a/dat/themerms.lua +++ b/dat/themerms.lua @@ -1,7 +1,10 @@ -- themerooms is an array of tables and/or functions. --- the tables define "frequency" and "contents", --- a plain function has frequency of 1 +-- the tables define "frequency", "contents", "mindiff" and "maxdiff". +-- frequency is optional; if omitted, 1 is assumed. +-- mindiff and maxdiff are optional and independent; if omitted, the room is +-- not constrained by level difficulty. +-- a plain function has frequency of 1, and no difficulty constraints. -- des.room({ type = "ordinary", filled = 1 }) -- - ordinary rooms can be converted to shops or any other special rooms. -- - filled = 1 means the room gets random room contents, even if it @@ -538,36 +541,45 @@ end }); }; -local total_frequency = 0; -for i = 1, #themerooms do - local t = type(themerooms[i]); +function is_eligible(room) + local t = type(room); + local diff = nh.level_difficulty(); if (t == "table") then - total_frequency = total_frequency + themerooms[i].frequency; + if (room.mindiff ~= nil and diff < room.mindiff) then + return false + elseif (room.maxdiff ~= nil and diff > room.maxdiff) then + return false + end elseif (t == "function") then - total_frequency = total_frequency + 1; + -- functions currently have no constraints end -end - -if (total_frequency == 0) then - error("Theme rooms total_frequency == 0"); + return true end function themerooms_generate() - local pick = nh.rn2(total_frequency); + local pick = 1; + local total_frequency = 0; for i = 1, #themerooms do - local t = type(themerooms[i]); - if (t == "table") then - pick = pick - themerooms[i].frequency; - if (pick < 0) then - themerooms[i].contents(); - return; + -- 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 + local this_frequency; + if (type(themerooms[i]) == "table" and themerooms[i].frequency ~= nil) then + this_frequency = themerooms[i].frequency; + else + this_frequency = 1; end - elseif (t == "function") then - pick = pick - 1; - if (pick < 0) then - themerooms[i](); - return; + total_frequency = total_frequency + this_frequency; + if (nh.rn2(total_frequency) < this_frequency) then + pick = i; end end end + + local t = type(themerooms[pick]); + if (t == "table") then + themerooms[pick].contents(); + elseif (t == "function") then + themerooms[pick](); + end end diff --git a/src/nhlua.c b/src/nhlua.c index 24bcc5d59..b3906592b 100644 --- a/src/nhlua.c +++ b/src/nhlua.c @@ -34,6 +34,7 @@ static int FDECL(nhl_ing_suffix, (lua_State *)); static int FDECL(nhl_an, (lua_State *)); static int FDECL(nhl_rn2, (lua_State *)); static int FDECL(nhl_random, (lua_State *)); +static int FDECL(nhl_level_difficulty, (lua_State *)); static void FDECL(init_nhc_data, (lua_State *)); static int FDECL(nhl_push_anything, (lua_State *, int, void *)); static int FDECL(nhl_meta_u_index, (lua_State *)); @@ -657,6 +658,21 @@ lua_State *L; return 1; } +/* level_difficulty() */ +static int +nhl_level_difficulty(L) +lua_State *L; +{ + int argc = lua_gettop(L); + if (argc == 0) { + lua_pushinteger(L, level_difficulty()); + } + else { + nhl_error(L, "level_difficulty should not have any args"); + } + return 1; +} + /* get mandatory integer value from table */ int get_table_int(L, name) @@ -831,6 +847,7 @@ static const struct luaL_Reg nhl_functions[] = { {"an", nhl_an}, {"rn2", nhl_rn2}, {"random", nhl_random}, + {"level_difficulty", nhl_level_difficulty}, {NULL, NULL} };