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.
This commit is contained in:
copperwater
2020-05-13 00:15:10 -04:00
committed by Pasi Kallinen
parent 5e9303f9df
commit c5b5a869d7
2 changed files with 52 additions and 23 deletions

View File

@@ -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

View File

@@ -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}
};