--- deliantra/server/random_maps/layout.C 2010/07/02 15:43:37 1.8 +++ deliantra/server/random_maps/layout.C 2010/07/03 00:39:57 1.10 @@ -25,11 +25,14 @@ #include #include -layout::layout (int w, int h) -: w(w), h(h) +void +layout::alloc (int w, int h) { assert (sizeof (cell) == 1); + this->w = w; + this->h = h; + // we store the layout in a single contiguous memory layout // first part consists of pointers to each column, followed // by the actual columns (not rows!) @@ -43,6 +46,18 @@ data [x] = p + x * h; } +layout::layout (int w, int h) +{ + alloc (w, h); +} + +layout::layout (layout ©) +{ + alloc (copy.w, copy.h); + + memcpy (data [0], copy.data [0], sizeof (cell) * h * w); +} + layout::~layout () { int size = (sizeof (cell *) + sizeof (cell) * h) * w; @@ -59,14 +74,25 @@ void layout::rect (int x1, int y1, int x2, int y2, char fill) { + --x2; + + memset (data [x1] + y1, fill, y2 - y1); + memset (data [x2] + y1, fill, y2 - y1); + + while (++x1 < x2) + data [x1][y1] = data [x1][y2 - 1] = fill; +} + +void +layout::fill_rect (int x1, int y1, int x2, int y2, char fill) +{ for (; x1 < x2; ++x1) memset (data [x1] + y1, fill, y2 - y1); } void layout::border (char fill) { - for (int i = 0; i < w; i++) data [i][0] = data [i][h - 1] = fill; - for (int j = 0; j < h; j++) data [0][j] = data [w - 1][j] = fill; + rect (0, 0, w, h, fill); } void @@ -158,7 +184,7 @@ typedef fixed_stack pointlist; -static noinline void +static void noinline push_flood_fill (layout &dist, pointlist &seeds, int x, int y) { if (dist [x][y]) @@ -189,20 +215,26 @@ { for (;;) { - if (x > 1 && U8 (dist [x - 1][y]) < d && dist [x - 1][y] > 1) - --x; - else if (x < dist.w - 2 && U8 (dist [x + 1][y]) < d && dist [x + 1][y] > 1) - ++x; - else if (y > 1 && U8 (dist [x][y - 1]) < d && dist [x][y - 1] > 1) - --y; - else if (y < dist.h - 2 && U8 (dist [x][y + 1]) < d && dist [x][y + 1] > 1) - ++y; - else - break; + point neigh[4]; + int ncnt = 0; + + if (x > 1 && U8 (dist [x - 1][y]) <= d && dist [x - 1][y] > 1) neigh [ncnt++] = point (x - 1, y); + if (x < dist.w - 2 && U8 (dist [x + 1][y]) <= d && dist [x + 1][y] > 1) neigh [ncnt++] = point (x + 1, y); + if (y > 1 && U8 (dist [x][y - 1]) <= d && dist [x][y - 1] > 1) neigh [ncnt++] = point (x, y - 1); + if (y < dist.h - 2 && U8 (dist [x][y + 1]) <= d && dist [x][y + 1] > 1) neigh [ncnt++] = point (x, y + 1); + + if (!ncnt) + return; + + point &p = neigh [rmg_rndm (ncnt)]; + + seeds.push (p); + + x = p.x; + y = p.y; d = dist [x][y]; dist [x][y] = 1; - seeds.push (point (x, y)); } } @@ -219,43 +251,35 @@ seeds.push (point (x, y)); } -void -layout::isolation_remover (bool dirty) +// isolation remover, works on a "distance" map +// the map must be initialised with 0 == rooms, 255 = walls +static void noinline +isolation_remover (layout &dist) { - layout dist (w, h); - // dist contains // 0 == invisited rooms // 1 == visited rooms // 2+ shortest distance to random near room - // phase 1, initialise dist array, find seed + // phase 1, find seed int cnt = 0; int x, y; - for (int i = 0; i < w; ++i) - for (int j = 0; j < h; ++j) - { - if (data [i][j] == '#') - dist [i][j] = U8 (255); - else - { - dist [i][j] = 0; - if (!rmg_rndm (++cnt)) - x = i, y = j; - } - } + for (int i = 0; i < dist.w; ++i) + for (int j = 0; j < dist.h; ++j) + if (!dist [i][j] && !rmg_rndm (++cnt)) + x = i, y = j; if (!cnt) { // map is completely massive, this is not good, // so make it empty instead. - clear (); - border (); + dist.clear (); + dist.border (255); return; } - fixed_stack seeds (w * h * 5); + fixed_stack seeds (dist.w * dist.h * 5); // found first free space - picking the first one gives // us a slight bias for tunnels, but usually you won't @@ -277,13 +301,8 @@ if (!dist [x][y]) { // found new isolated area, make tunnel - if (!dirty) - push_flood_fill (dist, seeds, x, y); - + push_flood_fill (dist, seeds, x, y); make_tunnel (dist, seeds, x, y, 255); - - if (dirty) - push_flood_fill (dist, seeds, x, y); } else { @@ -296,6 +315,18 @@ if (y > 0) maybe_push (dist, seeds, x, y - 1, d); } } +} + +void +layout::isolation_remover () +{ + layout dist (w, h); + + for (int x = 0; x < w; ++x) + for (int y = 0; y < h; ++y) + dist [x][y] = data [x][y] == '#' ? U8 (255) : 0; + + ::isolation_remover (dist); // now copy the tunnels over for (int x = 0; x < w; ++x) @@ -314,7 +345,7 @@ { // a rough cave case 0: - fill_rand (rmg_rndm (80, 95)); + fill_rand (rmg_rndm (85, 97)); break; // corridors @@ -867,16 +898,12 @@ { rmg_rndm.seed (time (0)); - for(int i=1;i<10;i) + for(int i=1;i<100;i++) { - layout maze (10, 10); - maze.fill_rand (90); + layout maze (40, 25); + maze.fill_rand (85); maze.border (); maze.isolation_remover (); - maze.doorify (); - maze.symmetrize (rmg_rndm (2, 4)); - maze.rotate (rmg_rndm (4)); - maze.expand2x (); maze.print (); }