/* * This file is part of Deliantra, the Roguelike Realtime MMORPG. * * Copyright (©) 2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team * * Deliantra is free software: you can redistribute it and/or modify it under * the terms of the Affero GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the Affero GNU General Public License * and the GNU General Public License along with this program. If not, see * . * * The authors can be reached via e-mail to */ #include #include LayoutData::LayoutData (int w, int h) : w(w), h(h) { int size = (sizeof (char *) + sizeof (char) * h) * w; col = (char **)salloc (size); char *data = (char *)(col + w); for (int x = w; x--; ) col [x] = data + x * h; } LayoutData::~LayoutData () { int size = (sizeof (char *) + sizeof (char) * h) * w; sfree ((char *)col, size); } void LayoutData::fill (char fill) { memset (col [0], fill, w * h); } void LayoutData::rect (int x1, int y1, int x2, int y2, char fill) { for (; x1 < x2; ++x1) memset (col [x1] + y1, fill, y2 - y1); } void LayoutData::border (char fill) { for (int i = 0; i < w; i++) col [i][0] = col [i][h - 1] = fill; for (int j = 0; j < h; j++) col [0][j] = col [w - 1][j] = fill; } ///////////////////////////////////////////////////////////////////////////// void Layout::print () { for (int y = 0; y < ptr->h; y++) { for (int x = 0; x < ptr->w; x++) { U8 c = (U8)ptr->col[x][y]; if (!c) c = ' '; else if (c < 10) c += '0'; else if (c < 32) c += 'a' - 10; putc ((char)c, stdout); } putc ('\n', stdout); } putc ('\n', stdout); } ///////////////////////////////////////////////////////////////////////////// // isolation remover - ensures single connected area typedef fixed_stack pointlist; static void room (Layout &layout, int xc, int yc) { layout->rect (xc - 2, yc - 2, xc + 3, yc + 3, 0); } static noinline void push_flood_fill (Layout maze, Layout dist, pointlist &seeds, int x, int y) { if (maze [x][y]) return; while (y > 0 && !maze [x][y - 1]) --y; int y0 = y; while (y < maze->h && !maze [x][y]) { seeds.push (point (x, y)); maze [x][y] = 1; dist [x][y] = 1; ++y; } while (--y >= y0) { if (x > 0) push_flood_fill (maze, dist, seeds, x - 1, y); if (x < maze->w - 1) push_flood_fill (maze, dist, seeds, x + 1, y); } } static inline void make_tunnel (Layout maze, Layout dist, pointlist &seeds, int x, int y, U8 d) { for (;;) { seeds.push (point (x, y)); maze [x][y] = 1; dist [x][y] = 1; if (x > 1 && U8 (dist [x - 1][y]) < d && dist [x - 1][y] > 1) --x; else if (x < maze->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 < maze->h - 2 && U8 (dist [x][y + 1]) < d && dist [x][y + 1] > 1) ++y; else break; d = dist [x][y]; } } static void isolation_remover (Layout maze, bool dirty) { Layout dist (maze->w, maze->h); dist->fill (255); fixed_stack seeds (maze->w * maze->h * 4); // phase 1, find seed for (int x = 1; x < maze->w; ++x) for (int y = 1; y < maze->h; ++y) if (!maze [x][y]) { seeds.push (point (x, y)); // phase 2, while we have seeds, if // seed is empty, floodfill, else grow while (seeds.size) { point p = seeds.remove (rmg_rndm (seeds.size)); x = p.x; y = p.y; if (!maze [x][y]) { // found new isolated area, make tunnel? if (!dirty) push_flood_fill (maze, dist, seeds, x, y); make_tunnel (maze, dist, seeds, x, y, 255); if (dirty) push_flood_fill (maze, dist, seeds, x, y); } else { U8 d = U8 (dist [x][y]) + 1; if (x < maze->w - 1 && U8 (dist [x + 1][y]) > d) { dist [x + 1][y] = d; seeds.push (point (x + 1, y)); } if (x > 0 && U8 (dist [x - 1][y]) > d) { dist [x - 1][y] = d; seeds.push (point (x - 1, y)); } if (y < maze->h - 1 && U8 (dist [x][y + 1]) > d) { dist [x][y + 1] = d; seeds.push (point (x, y + 1)); } if (y > 0 && U8 (dist [x][y - 1]) > d) { dist [x][y - 1] = d; seeds.push (point (x, y - 1)); } } } goto success; } success: dist.free (); // we mark free but visited floors as 1, undo this here for (int x = 0; x < maze->w; ++x) for (int y = 0; y < maze->h; ++y) if (maze [x][y] == 1) maze [x][y] = 0; } void LayoutData::isolation_remover (bool dirty) { Layout maze; maze.ptr = this; ::isolation_remover (maze, dirty); } #if 0 static struct demo { demo () { Layout maze (40, 25); rmg_rndm.seed (time (0)); for (int p = 80; p < 100; p += 1) { maze->fill ('#'); #if 1 for (int x = 1; x < maze->w - 1; ++x) for (int y = 1; y < maze->h - 1; ++y) maze [x][y] = rmg_rndm(100) < p ? '#' : 0; #else room (maze, 5, 5); room (maze, 30,20); room (maze, 10,20); room (maze, 20,10); #endif isolation_remover (maze, 1); maze.print (); } #if 0 for(int i=1;i<10;i) { maze_gen (maze, 1); maze.print (); } #endif exit (1); } } demo; #endif