diff --git a/dat/themerms.lua b/dat/themerms.lua index 4b2aeb05d..1c97015f2 100644 --- a/dat/themerms.lua +++ b/dat/themerms.lua @@ -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 diff --git a/doc/lua.adoc b/doc/lua.adoc index e0959480d..8b614bb4d 100644 --- a/doc/lua.adoc +++ b/doc/lua.adoc @@ -1070,6 +1070,20 @@ Example: local coord = selection.rndcoord(sel); local coord = selection.rndcoord(sel, 1); + +=== room + +Create a selection of locations inside the (current) room. + +Example: + + des.room({ type = "ordinary", contents = function(rm) + local sel = selection.room(); + des.terrain(sel, "I"); + end + }); + + === set Set the value for location (x,y) in the selection. diff --git a/include/extern.h b/include/extern.h index 32b88d989..bbb337376 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2612,6 +2612,7 @@ extern boolean get_coord(lua_State *, int, lua_Integer *, lua_Integer *); extern void cvt_to_abscoord(coordxy *, coordxy *); extern void cvt_to_relcoord(coordxy *, coordxy *); extern int nhl_abs_coord(lua_State *); +extern struct selectionvar *selection_from_mkroom(struct mkroom *); extern void update_croom(void); extern const char *get_trapname_bytype(int); extern void l_register_des(lua_State *); diff --git a/src/nhlsel.c b/src/nhlsel.c index 085ac5996..96ed226d2 100644 --- a/src/nhlsel.c +++ b/src/nhlsel.c @@ -17,6 +17,7 @@ static int l_selection_getpoint(lua_State *); static int l_selection_setpoint(lua_State *); static int l_selection_filter_percent(lua_State *); static int l_selection_rndcoord(lua_State *); +static int l_selection_room(lua_State *); static int l_selection_getbounds(lua_State *); static boolean params_sel_2coords(lua_State *, struct selectionvar **, coordxy *, coordxy *, coordxy *, coordxy *); @@ -383,6 +384,28 @@ l_selection_rndcoord(lua_State *L) return 1; } +/* local s = selection.room(); */ +static int +l_selection_room(lua_State *L) +{ + struct selectionvar *sel; + int argc = lua_gettop(L); + struct mkroom *croom = NULL; + + if (argc == 1) { + int i = luaL_checkinteger(L, -1); + + croom = (i >= 0 && i < g.nroom) ? &g.rooms[i] : NULL; + } + + sel = selection_from_mkroom(croom); + + l_selection_push_copy(L, sel); + selection_free(sel, TRUE); + + return 1; +} + /* local rect = sel:bounds(); */ static int l_selection_getbounds(lua_State *L) @@ -899,6 +922,7 @@ static const struct luaL_Reg l_selection_methods[] = { { "gradient", l_selection_gradient }, { "iterate", l_selection_iterate }, { "bounds", l_selection_getbounds }, + { "room", l_selection_room }, { NULL, NULL } }; diff --git a/src/sp_lev.c b/src/sp_lev.c index a546bc6e0..959cab09c 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -6056,7 +6056,8 @@ lspo_region(lua_State *L) lua_getfield(L, 1, "contents"); if (lua_type(L, -1) == LUA_TFUNCTION) { lua_remove(L, -2); - if (nhl_pcall(L, 0, 0)){ + l_push_mkroom_table(L, troom); + if (nhl_pcall(L, 1, 0)){ impossible("Lua error: %s", lua_tostring(L, -1)); } } else @@ -6664,6 +6665,25 @@ TODO: g.coder->croom needs to be updated return 0; } +struct selectionvar * +selection_from_mkroom(struct mkroom *croom) +{ + struct selectionvar *sel = selection_new(); + coordxy x, y; + + if (!croom && g.coder && g.coder->croom) + croom = g.coder->croom; + if (!croom) + return sel; + + for (y = croom->ly; y <= croom->hy; y++) + for (x = croom->lx; x <= croom->hx; x++) + if (isok(x, y) && !levl[x][y].edge + && levl[x][y].roomno == (croom - g.rooms) + ROOMOFFSET) + selection_setpoint(x, y, sel, 1); + return sel; +} + void update_croom(void) {