1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
|
------------------------------------------------------------------------------
-- geoelf.lua: The layout engine that generates layouts of
-- geometric rooms for the Elf branch
--
-- Main include for Geoelf layouts. This file (directly)
-- contains the functions for specifying rooms and possible
-- corridors, as well as the master function that runs the
-- layout once all the rooms and corridors have been specified.
------------------------------------------------------------------------------
geoelf = {} -- Main namespace for engine
-- This switch prints what is happening to console and the final
-- map at the end. Only advisable if you start tiles from a
-- command-line.
geoelf.debug = false
crawl_require("dlua/layout/geoelf_directions.lua")
crawl_require("dlua/layout/geoelf_glyphs.lua")
crawl_require("dlua/layout/geoelf_rooms.lua")
crawl_require("dlua/layout/geoelf_corridors.lua")
----------------------------------------------------------------
--
-- How to use the geoelf layout generator:
--
-- 1. Initialize the room and corridor arrays in the layout.
-- They will be needed as parameters to the subsequent
-- functions. Use the following code:
-- local room_data = { count=0 }
-- local corridor_data = { count=0 }
-- 2. Specify the rooms to include using the geoelf.add_room
-- function (see below for details).
-- 3. Specify the possible corridors between the rooms using
-- the geoelf.add_corridor function (see below for details).
-- -> Not all of these corridors will (normally) appear in
-- the final layout.
-- -> At most 1 corridor can be attached to any given room in
-- any given direction
-- -> These may be interspersed between the rooms as long as
-- both rooms the corridor is connected to have been
-- specified.
-- 4. Call the geoelf.generate function (see below for details).
--
-- The geoelf layout defines special meanings for the following
-- glyphs:
-- -> D, E, F: bush, plant, fungus (if enabled)
-- -> ~: glass door in glass
-- -> J, K, L, M, N, O: glass with pictures of tree, bush,
-- plant, fungus, statue, fountain
-- (needs special tiles)
--
-- If you need to connect the portion of the map generated
-- using geoelf layout generator ("the geoelf layout") to a
-- portion of the map generated in a different way ("your
-- layout"):
--
-- 1.-3. As above
-- 4. Use the add_room_dummy function to mark where on your
-- layout you want the geoelf layout to connect to
-- -> Nothing will be displayed for these rooms
-- 5. Add the possible corridors to connect the dummy rooms to
-- the geoelf layout
-- -> Not all corridors will appear, but at least one will
-- connect to each dummy room
-- -> If you connect the dummy rooms to each other, corridors
-- will be added between them as well
-- 6. Call the geoelf.generate function
--
----------------
--
-- add_room
--
-- Purpose: This function adds a room with the specified
-- properties to the list of rooms for the layout.
-- Parameter(s):
-- -> room_data: The array of room information. This new room
-- will be added to the array.
-- -> center_x
-- -> center_y: The x/y coordinates for the center of the room.
-- -> radius: The radius for the room. The exact meaning of
-- the radius varies depending on what shape the
-- room eventually has, but no part of the final
-- room will be farther than this (chessboard
-- distance) from the center.
-- Returns: The index of the room. You will need this to
-- specify possible corridors.
--
-- The room data structure has
-- -> center_x: The base x coordinate of the room center
-- -> center_y: The base y coordinate of the room center
-- -> radius: The size from room center to edge
-- -> zone: used to connect the rooms into a tree
-- -> corridor: An array of the corridors indexed using
-- direction values
-- -> is_dummy: Whether this room is a plaeholder
--
-- The elements in the array of room data are numbered from 0 as
-- in C++, not from 1 as recommended in LUA documentation. The
-- array also has a count field storing the number of elements.
--
function geoelf.add_room (room_data, center_x, center_y, radius)
if (room_data == nil) then
crawl.mpr("Error: No room data array")
end
if (room_data.count == nil) then
crawl.mpr("Error: Room data array does not have count")
end
if (center_x == nil) then
crawl.mpr("Error: center_x is nil")
end
if (center_y == nil) then
crawl.mpr("Error: center_y is nil")
end
if (radius == nil) then
crawl.mpr("Error: radius is nil")
end
if (geoelf.debug) then
print("geoelf.add_room(room_data, " .. center_x .. ", " .. center_y ..
", " .. radius .. ")")
end
local index = room_data.count
room_data[index] = {}
room_data[index].center_x = center_x
room_data[index].center_y = center_y
room_data[index].radius = radius
room_data[index].zone = index
room_data[index].corridor = {}
room_data[index].is_dummy = false
for i = 0, geoelf.directions.COUNT - 1 do
room_data[index].corridor[i] = nil
end
room_data.count = index + 1
return index
end
----------------
--
-- add_room_dummy
--
-- Purpose: This function adds a dummy room at the specified
-- position to the list of rooms for the layout. A
-- dummy room is connected into the tree but is never
-- drawn to the layout. Dummy rooms should be used to
-- connect the geoelf room system to parts of a map
-- generate using a different method.
-- Parameter(s):
-- -> room_data: The array of room information. This new room
-- will be added to the array.
-- -> center_x
-- -> center_y: The x/y coordinates for the center of the room.
-- Returns: The index of the room. You will need this to
-- specify possible corridors.
--
-- The room data structure is the same as for add_room
--
function geoelf.add_room_dummy (room_data, center_x, center_y)
if (room_data == nil) then
crawl.mpr("Error: No room data array")
end
if (room_data.count == nil) then
crawl.mpr("Error: Room data array does not have count")
end
if (center_x == nil) then
crawl.mpr("Error: center_x is nil")
end
if (center_y == nil) then
crawl.mpr("Error: center_y is nil")
end
if (geoelf.debug) then
print("geoelf.add_room_dummy(room_data, " .. center_x .. ", " ..
center_y .. ")")
end
local index = room_data.count
room_data[index] = {}
room_data[index].center_x = center_x
room_data[index].center_y = center_y
room_data[index].radius = 1
room_data[index].zone = index
room_data[index].corridor = {}
room_data[index].is_dummy = true
for i = 0, geoelf.directions.COUNT - 1 do
room_data[index].corridor[i] = nil
end
room_data.count = index + 1
return index
end
----------------
--
-- is_room_dummy
--
-- Purpose: To determine if the room with the specified index
-- is a dummy room dummy room.
-- Parameter(s):
-- -> room_data: The array of room information.
-- -> index: The index of the room
-- Returns: Whether the room in room_data with index index is a
-- dummy room.
--
function geoelf.is_room_dummy (room_data, index)
if (room_data == nil) then
crawl.mpr("Error: No room data array")
end
if (room_data.count == nil) then
crawl.mpr("Error: Room data array does not have count")
end
if (index == nil) then
crawl.mpr("Error: index is nil")
end
if (index >= room_data.count) then
crawl.mpr("Error: index is larger than room count")
end
return room_data[index].is_dummy
end
----------------
--
-- add_corridor
--
-- Purpose: This function adds a possible corridor to the list
-- for the layout with the specified properties.
-- Parameter(s):
-- -> room_data: The array of room information. The rooms with
-- corridor attaches to will be marked as
-- connected in that direction
-- -> corridor_data: The array of corridor information. The
-- new corridor will be added to this array
-- -> room1
-- -> room2: The indexes of the two rooms this corridor
-- connects
-- -> direction: The direction of the corridor from room1.
-- There is no sanity check here, so specifying
-- an incorrect direction can produce disconnect
-- layouts.
-- -> block_index: The index of the corridor that would
-- intersect this one. The block will also
-- apply in reverse. A corridor can block at
-- most one other corridor. The layout
-- generator will not include 2 corridors that
-- block each other.
-- Returns: The index of the corridor. You can also get this
-- from the room data array.
--
-- The corridor data structure has
-- -> room1: The room at one end
-- -> room2: The room at the other end
-- -> direction: The direction of the corridor from room 1
-- -> status: Whether the corridor is inactive, required
-- active, extra active, or blocked by another
-- -> block: The index of the corridor that this corridor
-- blocks, if any
--
-- The elements in the array of corridor data are numbered from
-- 0 as in C++, not from 1 as recommended in LUA documentation.
-- The array also has a count field storing the number of
-- elements.
--
function geoelf.add_corridor (room_data, corridor_data,
room1, room2,
direction, block_index)
if (room_data == nil) then
crawl.mpr("Error: No room data array")
end
if (room_data.count == nil) then
crawl.mpr("Error: Room data array does not have count")
end
if (corridor_data == nil) then
crawl.mpr("Error: No corridor data array")
end
if (corridor_data.count == nil) then
crawl.mpr("Error: Corridor data array does not have count")
end
if (room1 == nil) then
crawl.mpr("Error: room1 is nil")
end
if (room2 == nil) then
crawl.mpr("Error: room2 is nil")
end
if (direction == nil) then
crawl.mpr("Error: direction is nil")
end
if (geoelf.debug) then
if (block_index == nil) then
print("geoelf.add_corridor(room_data, corridor_data, " ..
room1 .. ", " .. room2 .. ", " .. direction .. ", nil)")
else
print("geoelf.add_corridor(room_data, corridor_data, " .. room1 ..
", " .. room2 .. ", " .. direction .. ", " .. block_index .. ")")
end
end
local index = corridor_data.count
corridor_data[index] = {}
corridor_data[index].room1 = room1
corridor_data[index].room2 = room2
corridor_data[index].direction = direction
corridor_data[index].status = geoelf.corridors.INACTIVE
corridor_data[index].block = block_index
if (block_index ~= nil) then
corridor_data[block_index].block = index
end
local reverse_direction = geoelf.directions.GET_REVERSE[direction]
room_data[room1].corridor[direction] = index
room_data[room2].corridor[reverse_direction] = index
corridor_data.count = index + 1
return index
end
----------------
--
-- generate
--
-- Purpose: This function generates the layout from the rooms
-- and possible corridors that have been specified.
-- Parameter(s):
-- -> e: A reference to the global environment. Pass in _G.
-- -> room_data: The array of room information
-- -> corridor_data: The array of corridor information
-- -> extra_fraction: The fraction of the extra corridors to
-- include beyond the minimum needed to
-- connect the map
-- -> fancy_room_fraction: The fraction of the rooms that have
-- ornamentation in them
-- -> only_trees: Whether all bushes, plants, and fungus should
-- be converted to trees
-- -> we may not want bushes, plants, fungus
-- -> they interfere with vault placement
-- -> trees do not interfere because they are considered to
-- be a terrain type instead of a monster
-- -> force_open_room_edge: Whether to force overwritten rooms
-- to have open floor at the new room
-- edge
-- -> the maps normally look better with this off, but can
-- become disconnected in a few very rare cases
-- Returns: N/A
--
function geoelf.generate (e, room_data, corridor_data,
extra_fraction, fancy_room_fraction,
only_trees, force_open_room_edge)
if (room_data == nil) then
crawl.mpr("Error: No room data array")
end
if (room_data.count == nil) then
crawl.mpr("Error: Room data array does not have count")
end
if (corridor_data == nil) then
crawl.mpr("Error: No corridor data array")
end
if (corridor_data.count == nil) then
crawl.mpr("Error: Corridor data array does not have count")
end
if (extra_fraction == nil) then
crawl.mpr("Error: extra_fraction is nil")
end
if (fancy_room_fraction == nil) then
crawl.mpr("Error: fancy_room_fraction is nil")
end
if (only_trees == nil) then
crawl.mpr("Error: only_trees is nil")
end
if (force_open_room_edge == nil) then
crawl.mpr("Error: force_open_room_edge is nil")
end
if (geoelf.debug) then
print("geoelf.generate(room_data, corridor_data, " .. extra_fraction .. ")")
end
-- choose corridors
if (geoelf.debug) then print("Choosing corridors:") end
geoelf.corridor_activate_tree (room_data, corridor_data)
geoelf.corridor_activate_random(room_data, corridor_data, extra_fraction)
-- draw corridors
if (geoelf.debug) then print("Drawing corridors:") end
for i = 0, corridor_data.count - 1 do
if (corridor_data[i].status == geoelf.corridors.ACTIVE) then
geoelf.corridors.draw(e, room_data, corridor_data, i)
end
end
-- choose a random order to draw rooms
if (geoelf.debug) then print("Choosing room draw order:") end
local draw_order = {}
for i = 0, room_data.count - 1 do
local other_index = crawl.random2(i + 1)
draw_order[i] = draw_order[other_index]
draw_order[other_index] = i
end
-- draw rooms
if (geoelf.debug) then print("Drawing rooms:") end
for i = 0, room_data.count - 1 do
if (not room_data[draw_order[i]].is_dummy) then
geoelf.rooms.draw(e, room_data, corridor_data, draw_order[i],
(crawl.random_real() < fancy_room_fraction),
force_open_room_edge)
end
end
-- substitutions
if (geoelf.debug) then print("Epilog:") end
e.subst(geoelf.glyphs.CORRIDOR .. " = " .. geoelf.glyphs.FLOOR)
--geoelf.beautify.remove_extra_doors(e)
e.remove_disconnected_doors {door = geoelf.glyphs.ALL_DOORS,
open = (geoelf.glyphs.ALL_FLOORLIKE ..
geoelf.glyphs.ALL_DOORS),
replace = geoelf.glyphs.FLOOR }
e.add_windows {wall = geoelf.glyphs.ALL_WALLLIKE,
open = geoelf.glyphs.ALL_FLOORLIKE,
window = geoelf.glyphs.GLASS }
geoelf.make_glass_doors(e)
geoelf.glyphs.assign_glyphs(e, only_trees)
e.subst(geoelf.glyphs.FOUNTAIN .. " = TTUV")
if (geoelf.debug) then
-- print the map to console
print("MAP")
local gxm, gym = dgn.max_bounds()
for y = 0, gym - 1 do
local line_output = ""
for x = 0, gxm - 1 do
line_output = line_output .. e.mapgrd[x][y]
end
print(line_output)
end
end
end
----------------
--
-- This is the helper function that turns the doors in glass
-- walls into glass doors. Some doors are made as glass
-- doors, but some are not. The ones that are not are fixed
-- here.
--
function geoelf.make_glass_doors (e)
if (geoelf.debug) then print(" geoelf.make_glass_doors") end
local gxm, gym = dgn.max_bounds()
for y = 1, gym - 2 do
for x = 1, gxm - 2 do
if (e.mapgrd[x][y] == geoelf.glyphs.DOOR) then
local south = e.mapgrd[x][y + 1]
local north = e.mapgrd[x][y - 1]
local east = e.mapgrd[x + 1][y]
local west = e.mapgrd[x - 1][y]
local southeast = e.mapgrd[x + 1][y + 1]
local southwest = e.mapgrd[x - 1][y + 1]
local northeast = e.mapgrd[x + 1][y - 1]
local northwest = e.mapgrd[x - 1][y - 1]
local any_wall = (south == geoelf.glyphs.WALL or
north == geoelf.glyphs.WALL or
east == geoelf.glyphs.WALL or
west == geoelf.glyphs.WALL or
southeast == geoelf.glyphs.WALL or
southwest == geoelf.glyphs.WALL or
northeast == geoelf.glyphs.WALL or
northwest == geoelf.glyphs.WALL)
local any_glass = (south == geoelf.glyphs.GLASS or
north == geoelf.glyphs.GLASS or
east == geoelf.glyphs.GLASS or
west == geoelf.glyphs.GLASS or
southeast == geoelf.glyphs.GLASS or
southwest == geoelf.glyphs.GLASS or
northeast == geoelf.glyphs.GLASS or
northwest == geoelf.glyphs.GLASS)
-- we turn the door into a glass door if it has glass
-- around it but no solid walls
if (not any_wall and any_glass) then
e.mapgrd[x][y] = geoelf.glyphs.GLASS_DOOR
end
end
end
end
end
----------------
--
-- These are helper functions that do not fit well into any
-- other geoelf file.
--
-- They are used to connect the tree of rooms.
--
function geoelf.room_absorb_zone (room_data, zone_old, zone_new)
if (room_data == nil) then
crawl.mpr("Error: No room data array")
end
if (room_data.count == nil) then
crawl.mpr("Error: Room data array does not have count")
end
for i = 0, room_data.count - 1 do
if (room_data[i].zone == zone_old) then
room_data[i].zone = zone_new
end
end
end
function geoelf.corridor_activate (corridor_data, index)
if (corridor_data == nil) then
crawl.mpr("Error: No corridor data array")
end
if (corridor_data.count == nil) then
crawl.mpr("Error: Corridor data array does not have count")
end
if (index >= corridor_data.count) then
crawl.mpr("Error: index is past the end of corridor_data")
end
if (corridor_data[index].status ~= geoelf.corridors.INACTIVE) then
crawl.mpr("Error: Corridor " .. index .. " is not inactive = " ..
geoelf.corridors.INACTIVE .. ", is " ..
corridor_data[index].status)
end
corridor_data[index].status = geoelf.corridors.ACTIVE
if (corridor_data[index].block ~= nil) then
local block_index = corridor_data[index].block
if (block_index >= corridor_data.count) then
crawl.mpr("Error: block_index is past the end of corridor_data")
end
if (corridor_data[block_index].status ~= geoelf.corridors.INACTIVE) then
crawl.mpr("Error: Attempting to block non-inactive corridor " ..
index .. " (status " .. corridor_data[block_index].status ..
")")
end
corridor_data[block_index].status = geoelf.corridors.BLOCKED
end
end
function geoelf.corridor_activate_tree (room_data, corridor_data)
if (room_data == nil) then
crawl.mpr("Error: No room data array")
end
if (room_data.count == nil) then
crawl.mpr("Error: Room data array does not have count")
end
if (corridor_data == nil) then
crawl.mpr("Error: No corridor data array")
end
if (corridor_data.count == nil) then
crawl.mpr("Error: Corridor data array does not have count")
end
-- choose a random order to try corridors
local test_order = {}
for i = 0, corridor_data.count - 1 do
local other_index = crawl.random2(i + 1)
test_order[i] = test_order[other_index]
test_order[other_index] = i
end
-- add required corridors in a random order
for i = 0, corridor_data.count - 1 do
local corridor_index = test_order[i]
if (corridor_data[corridor_index].status == geoelf.corridors.INACTIVE) then
local room_index1 = corridor_data[corridor_index].room1
local room_index2 = corridor_data[corridor_index].room2
local zone1 = room_data[room_index1].zone
local zone2 = room_data[room_index2].zone
if(zone1 ~= zone2) then
-- there is no way between these rooms yet
geoelf.corridor_activate(corridor_data, corridor_index)
geoelf.room_absorb_zone(room_data, zone2, zone1)
end
end
end
end
function geoelf.corridor_activate_random (room_data, corridor_data, fraction)
if (room_data == nil) then
crawl.mpr("Error: No room data array")
end
if (room_data.count == nil) then
crawl.mpr("Error: Room data array does not have count")
end
if (corridor_data == nil) then
crawl.mpr("Error: No corridor data array")
end
if (corridor_data.count == nil) then
crawl.mpr("Error: Corridor data array does not have count")
end
-- choose a random order to try inactive corridors
local test_order = { count=0 }
for i = 0, corridor_data.count - 1 do
if (corridor_data[i].status == geoelf.corridors.INACTIVE) then
local new_index = test_order.count
local other_index = crawl.random2(new_index + 1)
test_order[new_index] = test_order[other_index]
test_order[other_index] = i
test_order.count = new_index + 1
end
end
-- add extra corridors in a random order
for i = 0, (test_order.count * fraction) - 1 do
local corridor_index = test_order[i]
if (corridor_data[corridor_index].status == geoelf.corridors.INACTIVE) then
geoelf.corridor_activate(corridor_data, corridor_index)
end
end
end
|