1-- NetHack 3.7 Arch.des $NHDT-Date: 1432512784 2015/05/25 00:13:04 $ $NHDT-Branch: master $:$NHDT-Revision: 1.10 $ 2-- Copyright (c) 1989 by Jean-Christophe Collet 3-- Copyright (c) 1991 by M. Stephenson 4-- NetHack may be freely redistributed. See license for details. 5-- 6 7-- a bit kludgy... 8XMIN = 2 9XMAX = 76 10YMIN = 0 11YMAX = 19 12 13-- SHIM -- g.xstart is 1, usually 14function getmap(x, y) 15 return nh.getmap(x+1, y) 16end 17 18des.level_flags("noflip", "noteleport", "hardfloor", "inaccessibles", "nommap") 19 20-- Non diggable walls 21des.non_diggable(selection.area(00,00,76,20)) 22 23-- initial room containing the upstairs 24des.room({ type = "ordinary", lit = 0, x = 28 + nh.rn2(20), y = 1 + nh.rn2(14), 25 contents = function(rm) 26 des.stair("up") 27 end 28}); 29-- find upstairs location for use later 30local ustairx, ustairy 31selection.area(00,00,75,19):iterate(function(x, y) 32 if getmap(x, y)['typ_name'] == 'stairs' then 33 ustairx = x 34 ustairy = y 35 end 36end) 37 38-- Return a rectangular selection for a room that can be overlaid entirely 39-- onto stone or existing walls. 40-- One of dx and dy must be zero and the other must be either 1 or -1. 41-- If a room of at least 2x2 won't fit here, return all -1 coordinates. 42function findroom(xi, yi, maindir, straightline) 43 if xi <= XMIN or yi <= YMIN or xi >= XMAX or yi >= YMAX then 44 return nil 45 end 46 local dirs = { north = true, south = true, east = true, west = true} 47 -- don't grow backwards 48 if maindir == "north" then dirs["south"] = false end 49 if maindir == "south" then dirs["north"] = false end 50 if maindir == "east" then dirs["west"] = false end 51 if maindir == "west" then dirs["east"] = false end 52 if straightline then 53 dirs = { } 54 dirs[maindir] = true 55 end 56 57 function rnddir() 58 local elig = 0 59 local sel = nil 60 for dir, bool in pairs(dirs) do 61 if bool then 62 elig = elig + 1 63 if nh.rn2(elig) == 0 then 64 sel = dir 65 end 66 end 67 end 68 return sel 69 end 70 71 local sel = selection.new() 72 sel:set(xi, yi, 1) 73 74 local nexpand = d(2,6) 75 local growths = 0 76 77 -- bias against lots of tall vertical corridors 78 if straightline and (maindir == "north" or maindir == "south") and percent(60) then 79 nexpand = math.ceil(nexpand / 2) 80 end 81 82 for i=1,nexpand do 83 -- randomly pick a direction to grow 84 local currdir = rnddir() 85 if currdir == nil then 86 break 87 end 88 if i == 1 then 89 -- rooms should be at least 2 deep, so force this on the first attempt 90 currdir = maindir 91 end 92 93 local newsel = sel:clone():grow(currdir) 94 local addition = (newsel ~ sel) -- set subtraction would be better, but xor will do 95 local invalid = false 96 addition:iterate(function(x, y) 97 -- don't allow it to go right up against the edge of the map; leave 98 -- room for the wall 99 if x == XMIN or y == YMIN or x == XMAX or y == YMAX then 100 invalid = true 101 end 102 if getmap(x, y)["typ_name"] ~= "stone" then 103 invalid = true 104 end 105 end) 106 if invalid then 107 -- can't grow in this direction 108 dirs[currdir] = false 109 -- potential extension: in this case, cut down on nexpand 110 else 111 -- can grow 112 sel = newsel 113 growths = growths + 1 114 end 115 end 116 117 -- needs to have grown a certain number of times 118 -- possible extension: check whether a !straightline room has grown 119 -- perpendicularly (not always in maindir) 120 if growths > 3 or straightline then 121 return sel 122 else 123 return nil 124 end 125end 126 127ALLDIRS = { "north", "south", "east", "west" } 128 129local snakerooms = {} 130 131for i=1,60 do 132 -- Find an available spot on a wall where a door can be placed leading into a 133 -- corridor or another room. 134 local wallsN = selection.match(" \nw\n.") 135 local wallsS = selection.match(".\nw\n ") 136 local wallsE = selection.match(".w ") 137 local wallsW = selection.match(" w.") 138 local allwalls = wallsN | wallsS | wallsE | wallsW 139 local wx, wy = allwalls:rndcoord(1) -- remove this spot from allwalls 140 141 -- des.terrain({ selection = wallsN, typ="}" }) 142 local north, south, east, west = false, false, false, false 143 local dx, dy = 0, 0 144 if wallsN:get(wx, wy) > 0 then 145 dir, dx, dy = "north", 0, -1 146 elseif wallsS:get(wx, wy) > 0 then 147 dir, dx, dy = "south", 0, 1 148 elseif wallsE:get(wx, wy) > 0 then 149 dir, dx, dy = "east", 1, 0 150 elseif wallsW:get(wx, wy) > 0 then 151 dir, dx, dy = "west", -1, 0 152 end 153 154 local tryroom = nil 155 if percent(25) then 156 tryroom = findroom(wx+dx, wy+dy, dir, false) 157 end 158 if tryroom == nil then 159 -- room failed. try a line? 160 tryroom = findroom(wx+dx, wy+dy, dir, true) 161 else 162 -- room succeeded; mark it as a candidate for being filled with snakes 163 snakerooms[#snakerooms+1] = tryroom:clone() 164 end 165 if tryroom ~= nil then 166 -- to make walls, grow in all directions 167 local expanded = tryroom:clone():grow("all") 168 des.terrain({ selection=expanded, typ='-' }) -- this will overwrite wx,wy but that's fine 169 des.terrain({ selection=tryroom, typ='.' }) 170 171 local doorstate = percent(50) and "secret" or "random" 172 des.door(doorstate, wx, wy) 173 end 174end 175 176-- possible extension: match things like 177-- |.| 178-- --- 179-- x.x 180-- to create a door on them in post-proc 181 182local deadends = get_deadends() 183 184-- guard against very rare case of no dead ends 185local n_ends = 0 186deadends:iterate(function(x, y) 187 n_ends = n_ends + 1 188end) 189 190if n_ends > 0 then 191 -- Attempt to place the downstair sufficiently far away from the upstair. 192 local stairell = selection.ellipse(ustairx, ustairy, 20, 10, 1):negate() 193 local far_ends = stairell & deadends 194 local dstairx,dstairy = far_ends:rndcoord() 195 if dstairx < 0 or dstairy < 0 then 196 -- pick any dead end; potential future extension is to find the furthest 197 -- one away 198 dstairx, dstairy = deadends:rndcoord() 199 end 200 des.stair({ dir = "down", coord = {dstairx, dstairy} }) 201else 202 des.stair("down") 203end 204 205-- traps 206for i=1,30 do 207 des.trap({ no_spider_on_web = true }) 208end 209 210-- mummies and other enemies 211local n_mummies = 10 + d(4,4) 212local mummies_in_ends = math.min(math.floor(n_ends * 3 / 4), n_mummies) 213function mkmummy(deadend) 214 local template = { class='M', id='human mummy', waiting=1, coord={deadends:rndcoord()} } 215 if not deadend then 216 template['coord'] = nil 217 end 218 if percent(10) then 219 template['id'] = nil -- generate any M randomly 220 end 221 des.monster(template) 222end 223 224for i=1, mummies_in_ends do 225 mkmummy(true) 226 mummies_in_ends = mummies_in_ends - 1 227 n_mummies = n_mummies - 1 228end 229-- then any remaining mummies 230for i=1,n_mummies do 231 mkmummy(false) 232end 233 234-- "Indy, why does the floor move?" 235local snaketypes = { 'pit viper', 'cobra' } 236local snk_room = #snakerooms > 0 and d(#snakerooms) or nil 237-- des.terrain({ selection = snakerooms[snk_room], typ='L' }) 238for i=1,4+d(6) do 239 local snkid = snaketypes[d(#snaketypes)] 240 if snk_room == nil then 241 des.monster(snkid) 242 else 243 des.monster({ id=snkid, coord={snakerooms[snk_room]:rndcoord()} }) 244 end 245end 246 247bats = { "bat", "giant bat", "vampire bat" } 248for i=1,3 do 249 des.monster(bats[d(#bats)]) 250end 251 252-- treasure! 253for i = 1,10+d(10) do 254 des.object({ id="gold piece", quantity = d(250) }) 255end 256for i=1,2+d(3) do 257 local template = { id="chest", coord={deadends:rndcoord()} } 258 if percent(50) then 259 -- make it a mummy trap 260 template['trapped'] = 1 261 template['spe'] = 3 262 template['material'] = 'gold' 263 elseif percent(30) then 264 template['material'] = 'gold' 265 end 266 des.object(template) 267end 268local wpn_mats = { "copper", "silver", "gold", "bone" } 269local wpns = { "short sword", "dagger", "knife", "spear", "javelin", "quarterstaff", "axe", "flail" } 270for i=1,d(2,4) do 271 des.object({ id=wpns[d(#wpns)], material=wpn_mats[d(#wpn_mats)], coord={deadends:rndcoord()} }) 272end 273 274