dungeongen.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. /*
  2. Minetest
  3. Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include "dungeongen.h"
  17. #include "mapgen.h"
  18. #include "voxel.h"
  19. #include "noise.h"
  20. #include "mapblock.h"
  21. #include "mapnode.h"
  22. #include "map.h"
  23. #include "nodedef.h"
  24. #include "profiler.h"
  25. #include "settings.h" // For g_settings
  26. #include "main.h" // For g_profiler
  27. //#define DGEN_USE_TORCHES
  28. NoiseParams nparams_dungeon_rarity(0.0, 1.0, v3f(500.0, 500.0, 500.0), 0, 2, 0.8);
  29. NoiseParams nparams_dungeon_wetness(0.0, 1.0, v3f(40.0, 40.0, 40.0), 32474, 4, 1.1);
  30. NoiseParams nparams_dungeon_density(0.0, 1.0, v3f(2.5, 2.5, 2.5), 0, 2, 1.4);
  31. ///////////////////////////////////////////////////////////////////////////////
  32. DungeonGen::DungeonGen(Mapgen *mapgen, DungeonParams *dparams) {
  33. this->mg = mapgen;
  34. this->vm = mapgen->vm;
  35. #ifdef DGEN_USE_TORCHES
  36. c_torch = ndef->getId("default:torch");
  37. #endif
  38. if (dparams) {
  39. memcpy(&dp, dparams, sizeof(dp));
  40. } else {
  41. dp.c_water = mg->ndef->getId("mapgen_water_source");
  42. dp.c_cobble = mg->ndef->getId("mapgen_cobble");
  43. dp.c_moss = mg->ndef->getId("mapgen_mossycobble");
  44. dp.c_stair = mg->ndef->getId("mapgen_stair_cobble");
  45. dp.diagonal_dirs = false;
  46. dp.mossratio = 3.0;
  47. dp.holesize = v3s16(1, 2, 1);
  48. dp.roomsize = v3s16(0,0,0);
  49. dp.notifytype = GENNOTIFY_DUNGEON;
  50. dp.np_rarity = nparams_dungeon_rarity;
  51. dp.np_wetness = nparams_dungeon_wetness;
  52. dp.np_density = nparams_dungeon_density;
  53. }
  54. }
  55. void DungeonGen::generate(u32 bseed, v3s16 nmin, v3s16 nmax) {
  56. //TimeTaker t("gen dungeons");
  57. int approx_groundlevel = 10 + mg->water_level;
  58. if ((nmin.Y + nmax.Y) / 2 >= approx_groundlevel ||
  59. NoisePerlin3D(&dp.np_rarity, nmin.X, nmin.Y, nmin.Z, mg->seed) < 0.2)
  60. return;
  61. this->blockseed = bseed;
  62. random.seed(bseed + 2);
  63. // Dungeon generator doesn't modify places which have this set
  64. vm->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE | VMANIP_FLAG_DUNGEON_PRESERVE);
  65. // Set all air and water to be untouchable to make dungeons open
  66. // to caves and open air
  67. for (s16 z = nmin.Z; z <= nmax.Z; z++) {
  68. for (s16 y = nmin.Y; y <= nmax.Y; y++) {
  69. u32 i = vm->m_area.index(nmin.X, y, z);
  70. for (s16 x = nmin.X; x <= nmax.X; x++) {
  71. content_t c = vm->m_data[i].getContent();
  72. if (c == CONTENT_AIR || c == dp.c_water)
  73. vm->m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
  74. i++;
  75. }
  76. }
  77. }
  78. // Add it
  79. makeDungeon(v3s16(1,1,1) * MAP_BLOCKSIZE);
  80. // Convert some cobble to mossy cobble
  81. if (dp.mossratio != 0.0) {
  82. for (s16 z = nmin.Z; z <= nmax.Z; z++)
  83. for (s16 y = nmin.Y; y <= nmax.Y; y++) {
  84. u32 i = vm->m_area.index(nmin.X, y, z);
  85. for (s16 x = nmin.X; x <= nmax.X; x++) {
  86. if (vm->m_data[i].getContent() == dp.c_cobble) {
  87. float wetness = NoisePerlin3D(&dp.np_wetness, x, y, z, mg->seed);
  88. float density = NoisePerlin3D(&dp.np_density, x, y, z, blockseed);
  89. if (density < wetness / dp.mossratio)
  90. vm->m_data[i].setContent(dp.c_moss);
  91. }
  92. i++;
  93. }
  94. }
  95. }
  96. //printf("== gen dungeons: %dms\n", t.stop());
  97. }
  98. void DungeonGen::makeDungeon(v3s16 start_padding)
  99. {
  100. v3s16 areasize = vm->m_area.getExtent();
  101. v3s16 roomsize;
  102. v3s16 roomplace;
  103. /*
  104. Find place for first room
  105. */
  106. bool fits = false;
  107. for (u32 i = 0; i < 100 && !fits; i++)
  108. {
  109. bool is_large_room = ((random.next() & 3) == 1);
  110. roomsize = is_large_room ?
  111. v3s16(random.range(8, 16),random.range(8, 16),random.range(8, 16)) :
  112. v3s16(random.range(4, 8),random.range(4, 6),random.range(4, 8));
  113. roomsize += dp.roomsize;
  114. // start_padding is used to disallow starting the generation of
  115. // a dungeon in a neighboring generation chunk
  116. roomplace = vm->m_area.MinEdge + start_padding + v3s16(
  117. random.range(0,areasize.X-roomsize.X-1-start_padding.X),
  118. random.range(0,areasize.Y-roomsize.Y-1-start_padding.Y),
  119. random.range(0,areasize.Z-roomsize.Z-1-start_padding.Z));
  120. /*
  121. Check that we're not putting the room to an unknown place,
  122. otherwise it might end up floating in the air
  123. */
  124. fits = true;
  125. for (s16 z = 1; z < roomsize.Z - 1; z++)
  126. for (s16 y = 1; y < roomsize.Y - 1; y++)
  127. for (s16 x = 1; x < roomsize.X - 1; x++)
  128. {
  129. v3s16 p = roomplace + v3s16(x, y, z);
  130. u32 vi = vm->m_area.index(p);
  131. if ((vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_INSIDE) ||
  132. vm->m_data[vi].getContent() == CONTENT_IGNORE) {
  133. fits = false;
  134. break;
  135. }
  136. }
  137. }
  138. // No place found
  139. if (fits == false)
  140. return;
  141. /*
  142. Stores the center position of the last room made, so that
  143. a new corridor can be started from the last room instead of
  144. the new room, if chosen so.
  145. */
  146. v3s16 last_room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2);
  147. u32 room_count = random.range(2, 16);
  148. for (u32 i = 0; i < room_count; i++)
  149. {
  150. // Make a room to the determined place
  151. makeRoom(roomsize, roomplace);
  152. v3s16 room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2);
  153. if (mg->gennotify & (1 << dp.notifytype)) {
  154. std::vector <v3s16> *nvec = mg->gen_notifications[dp.notifytype];
  155. nvec->push_back(room_center);
  156. }
  157. #ifdef DGEN_USE_TORCHES
  158. // Place torch at room center (for testing)
  159. vm->m_data[vm->m_area.index(room_center)] = MapNode(c_torch);
  160. #endif
  161. // Quit if last room
  162. if (i == room_count - 1)
  163. break;
  164. // Determine walker start position
  165. bool start_in_last_room = (random.range(0, 2) != 0);
  166. v3s16 walker_start_place;
  167. if (start_in_last_room) {
  168. walker_start_place = last_room_center;
  169. } else {
  170. walker_start_place = room_center;
  171. // Store center of current room as the last one
  172. last_room_center = room_center;
  173. }
  174. // Create walker and find a place for a door
  175. v3s16 doorplace;
  176. v3s16 doordir;
  177. m_pos = walker_start_place;
  178. if (!findPlaceForDoor(doorplace, doordir))
  179. return;
  180. if (random.range(0,1) == 0)
  181. // Make the door
  182. makeDoor(doorplace, doordir);
  183. else
  184. // Don't actually make a door
  185. doorplace -= doordir;
  186. // Make a random corridor starting from the door
  187. v3s16 corridor_end;
  188. v3s16 corridor_end_dir;
  189. makeCorridor(doorplace, doordir, corridor_end, corridor_end_dir);
  190. // Find a place for a random sized room
  191. roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8));
  192. roomsize += dp.roomsize;
  193. m_pos = corridor_end;
  194. m_dir = corridor_end_dir;
  195. if (!findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace))
  196. return;
  197. if (random.range(0,1) == 0)
  198. // Make the door
  199. makeDoor(doorplace, doordir);
  200. else
  201. // Don't actually make a door
  202. roomplace -= doordir;
  203. }
  204. }
  205. void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
  206. {
  207. MapNode n_cobble(dp.c_cobble);
  208. MapNode n_air(CONTENT_AIR);
  209. // Make +-X walls
  210. for (s16 z = 0; z < roomsize.Z; z++)
  211. for (s16 y = 0; y < roomsize.Y; y++)
  212. {
  213. {
  214. v3s16 p = roomplace + v3s16(0, y, z);
  215. if (vm->m_area.contains(p) == false)
  216. continue;
  217. u32 vi = vm->m_area.index(p);
  218. if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
  219. continue;
  220. vm->m_data[vi] = n_cobble;
  221. }
  222. {
  223. v3s16 p = roomplace + v3s16(roomsize.X - 1, y, z);
  224. if (vm->m_area.contains(p) == false)
  225. continue;
  226. u32 vi = vm->m_area.index(p);
  227. if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
  228. continue;
  229. vm->m_data[vi] = n_cobble;
  230. }
  231. }
  232. // Make +-Z walls
  233. for (s16 x = 0; x < roomsize.X; x++)
  234. for (s16 y = 0; y < roomsize.Y; y++)
  235. {
  236. {
  237. v3s16 p = roomplace + v3s16(x, y, 0);
  238. if (vm->m_area.contains(p) == false)
  239. continue;
  240. u32 vi = vm->m_area.index(p);
  241. if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
  242. continue;
  243. vm->m_data[vi] = n_cobble;
  244. }
  245. {
  246. v3s16 p = roomplace + v3s16(x, y, roomsize.Z - 1);
  247. if (vm->m_area.contains(p) == false)
  248. continue;
  249. u32 vi = vm->m_area.index(p);
  250. if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
  251. continue;
  252. vm->m_data[vi] = n_cobble;
  253. }
  254. }
  255. // Make +-Y walls (floor and ceiling)
  256. for (s16 z = 0; z < roomsize.Z; z++)
  257. for (s16 x = 0; x < roomsize.X; x++)
  258. {
  259. {
  260. v3s16 p = roomplace + v3s16(x, 0, z);
  261. if (vm->m_area.contains(p) == false)
  262. continue;
  263. u32 vi = vm->m_area.index(p);
  264. if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
  265. continue;
  266. vm->m_data[vi] = n_cobble;
  267. }
  268. {
  269. v3s16 p = roomplace + v3s16(x,roomsize. Y - 1, z);
  270. if (vm->m_area.contains(p) == false)
  271. continue;
  272. u32 vi = vm->m_area.index(p);
  273. if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
  274. continue;
  275. vm->m_data[vi] = n_cobble;
  276. }
  277. }
  278. // Fill with air
  279. for (s16 z = 1; z < roomsize.Z - 1; z++)
  280. for (s16 y = 1; y < roomsize.Y - 1; y++)
  281. for (s16 x = 1; x < roomsize.X - 1; x++)
  282. {
  283. v3s16 p = roomplace + v3s16(x, y, z);
  284. if (vm->m_area.contains(p) == false)
  285. continue;
  286. u32 vi = vm->m_area.index(p);
  287. vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
  288. vm->m_data[vi] = n_air;
  289. }
  290. }
  291. void DungeonGen::makeFill(v3s16 place, v3s16 size,
  292. u8 avoid_flags, MapNode n, u8 or_flags)
  293. {
  294. for (s16 z = 0; z < size.Z; z++)
  295. for (s16 y = 0; y < size.Y; y++)
  296. for (s16 x = 0; x < size.X; x++)
  297. {
  298. v3s16 p = place + v3s16(x, y, z);
  299. if (vm->m_area.contains(p) == false)
  300. continue;
  301. u32 vi = vm->m_area.index(p);
  302. if (vm->m_flags[vi] & avoid_flags)
  303. continue;
  304. vm->m_flags[vi] |= or_flags;
  305. vm->m_data[vi] = n;
  306. }
  307. }
  308. void DungeonGen::makeHole(v3s16 place)
  309. {
  310. makeFill(place, dp.holesize, 0,
  311. MapNode(CONTENT_AIR), VMANIP_FLAG_DUNGEON_INSIDE);
  312. }
  313. void DungeonGen::makeDoor(v3s16 doorplace, v3s16 doordir)
  314. {
  315. makeHole(doorplace);
  316. #ifdef DGEN_USE_TORCHES
  317. // Place torch (for testing)
  318. vm->m_data[vm->m_area.index(doorplace)] = MapNode(c_torch);
  319. #endif
  320. }
  321. void DungeonGen::makeCorridor(v3s16 doorplace,
  322. v3s16 doordir, v3s16 &result_place, v3s16 &result_dir)
  323. {
  324. makeHole(doorplace);
  325. v3s16 p0 = doorplace;
  326. v3s16 dir = doordir;
  327. u32 length;
  328. /*if (random.next() % 2)
  329. length = random.range(1, 13);
  330. else
  331. length = random.range(1, 6);*/
  332. length = random.range(1, 13);
  333. u32 partlength = random.range(1, 13);
  334. u32 partcount = 0;
  335. s16 make_stairs = 0;
  336. if (random.next() % 2 == 0 && partlength >= 3)
  337. make_stairs = random.next() % 2 ? 1 : -1;
  338. for (u32 i = 0; i < length; i++) {
  339. v3s16 p = p0 + dir;
  340. if (partcount != 0)
  341. p.Y += make_stairs;
  342. if (vm->m_area.contains(p) == true &&
  343. vm->m_area.contains(p + v3s16(0, 1, 0)) == true) {
  344. if (make_stairs) {
  345. makeFill(p + v3s16(-1, -1, -1), dp.holesize + v3s16(2, 3, 2),
  346. VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(dp.c_cobble), 0);
  347. makeHole(p);
  348. makeHole(p - dir);
  349. // TODO: fix stairs code so it works 100% (quite difficult)
  350. // exclude stairs from the bottom step
  351. // exclude stairs from diagonal steps
  352. if (((dir.X ^ dir.Z) & 1) &&
  353. (((make_stairs == 1) && i != 0) ||
  354. ((make_stairs == -1) && i != length - 1))) {
  355. // rotate face 180 deg if making stairs backwards
  356. int facedir = dir_to_facedir(dir * make_stairs);
  357. u32 vi = vm->m_area.index(p.X - dir.X, p.Y - 1, p.Z - dir.Z);
  358. if (vm->m_data[vi].getContent() == dp.c_cobble)
  359. vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
  360. vi = vm->m_area.index(p.X, p.Y, p.Z);
  361. if (vm->m_data[vi].getContent() == dp.c_cobble)
  362. vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
  363. }
  364. } else {
  365. makeFill(p + v3s16(-1, -1, -1), dp.holesize + v3s16(2, 2, 2),
  366. VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(dp.c_cobble), 0);
  367. makeHole(p);
  368. }
  369. p0 = p;
  370. } else {
  371. // Can't go here, turn away
  372. dir = turn_xz(dir, random.range(0, 1));
  373. make_stairs = -make_stairs;
  374. partcount = 0;
  375. partlength = random.range(1, length);
  376. continue;
  377. }
  378. partcount++;
  379. if (partcount >= partlength) {
  380. partcount = 0;
  381. dir = random_turn(random, dir);
  382. partlength = random.range(1,length);
  383. make_stairs = 0;
  384. if (random.next() % 2 == 0 && partlength >= 3)
  385. make_stairs = random.next() % 2 ? 1 : -1;
  386. }
  387. }
  388. result_place = p0;
  389. result_dir = dir;
  390. }
  391. bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
  392. {
  393. for (u32 i = 0; i < 100; i++)
  394. {
  395. v3s16 p = m_pos + m_dir;
  396. v3s16 p1 = p + v3s16(0, 1, 0);
  397. if (vm->m_area.contains(p) == false
  398. || vm->m_area.contains(p1) == false
  399. || i % 4 == 0)
  400. {
  401. randomizeDir();
  402. continue;
  403. }
  404. if (vm->getNodeNoExNoEmerge(p).getContent() == dp.c_cobble
  405. && vm->getNodeNoExNoEmerge(p1).getContent() == dp.c_cobble)
  406. {
  407. // Found wall, this is a good place!
  408. result_place = p;
  409. result_dir = m_dir;
  410. // Randomize next direction
  411. randomizeDir();
  412. return true;
  413. }
  414. /*
  415. Determine where to move next
  416. */
  417. // Jump one up if the actual space is there
  418. if (vm->getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() == dp.c_cobble
  419. && vm->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() == CONTENT_AIR
  420. && vm->getNodeNoExNoEmerge(p+v3s16(0,2,0)).getContent() == CONTENT_AIR)
  421. p += v3s16(0,1,0);
  422. // Jump one down if the actual space is there
  423. if (vm->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() == dp.c_cobble
  424. && vm->getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() == CONTENT_AIR
  425. && vm->getNodeNoExNoEmerge(p+v3s16(0,-1,0)).getContent() == CONTENT_AIR)
  426. p += v3s16(0,-1,0);
  427. // Check if walking is now possible
  428. if (vm->getNodeNoExNoEmerge(p).getContent() != CONTENT_AIR
  429. || vm->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() != CONTENT_AIR)
  430. {
  431. // Cannot continue walking here
  432. randomizeDir();
  433. continue;
  434. }
  435. // Move there
  436. m_pos = p;
  437. }
  438. return false;
  439. }
  440. bool DungeonGen::findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
  441. v3s16 &result_doordir, v3s16 &result_roomplace)
  442. {
  443. for (s16 trycount = 0; trycount < 30; trycount++)
  444. {
  445. v3s16 doorplace;
  446. v3s16 doordir;
  447. bool r = findPlaceForDoor(doorplace, doordir);
  448. if (r == false)
  449. continue;
  450. v3s16 roomplace;
  451. // X east, Z north, Y up
  452. #if 1
  453. if (doordir == v3s16(1, 0, 0)) // X+
  454. roomplace = doorplace +
  455. v3s16(0, -1, random.range(-roomsize.Z + 2, -2));
  456. if (doordir == v3s16(-1, 0, 0)) // X-
  457. roomplace = doorplace +
  458. v3s16(-roomsize.X + 1, -1, random.range(-roomsize.Z + 2, -2));
  459. if (doordir == v3s16(0, 0, 1)) // Z+
  460. roomplace = doorplace +
  461. v3s16(random.range(-roomsize.X + 2, -2), -1, 0);
  462. if (doordir == v3s16(0, 0, -1)) // Z-
  463. roomplace = doorplace +
  464. v3s16(random.range(-roomsize.X + 2, -2), -1, -roomsize.Z + 1);
  465. #endif
  466. #if 0
  467. if (doordir == v3s16(1, 0, 0)) // X+
  468. roomplace = doorplace + v3s16(0, -1, -roomsize.Z / 2);
  469. if (doordir == v3s16(-1, 0, 0)) // X-
  470. roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z / 2);
  471. if (doordir == v3s16(0, 0, 1)) // Z+
  472. roomplace = doorplace + v3s16(-roomsize.X / 2, -1, 0);
  473. if (doordir == v3s16(0, 0, -1)) // Z-
  474. roomplace = doorplace + v3s16(-roomsize.X / 2, -1, -roomsize.Z + 1);
  475. #endif
  476. // Check fit
  477. bool fits = true;
  478. for (s16 z = 1; z < roomsize.Z - 1; z++)
  479. for (s16 y = 1; y < roomsize.Y - 1; y++)
  480. for (s16 x = 1; x < roomsize.X - 1; x++)
  481. {
  482. v3s16 p = roomplace + v3s16(x, y, z);
  483. if (vm->m_area.contains(p) == false)
  484. {
  485. fits = false;
  486. break;
  487. }
  488. if (vm->m_flags[vm->m_area.index(p)]
  489. & VMANIP_FLAG_DUNGEON_INSIDE)
  490. {
  491. fits = false;
  492. break;
  493. }
  494. }
  495. if(fits == false)
  496. {
  497. // Find new place
  498. continue;
  499. }
  500. result_doorplace = doorplace;
  501. result_doordir = doordir;
  502. result_roomplace = roomplace;
  503. return true;
  504. }
  505. return false;
  506. }
  507. v3s16 rand_ortho_dir(PseudoRandom &random, bool diagonal_dirs)
  508. {
  509. // Make diagonal directions somewhat rare
  510. if (diagonal_dirs && (random.next() % 4 == 0)) {
  511. v3s16 dir;
  512. int trycount = 0;
  513. do {
  514. trycount++;
  515. dir = v3s16(random.next() % 3 - 1, 0, random.next() % 3 - 1);
  516. } while ((dir.X == 0 && dir.Z == 0) && trycount < 10);
  517. return dir;
  518. } else {
  519. if (random.next() % 2 == 0)
  520. return random.next() % 2 ? v3s16(-1, 0, 0) : v3s16(1, 0, 0);
  521. else
  522. return random.next() % 2 ? v3s16(0, 0, -1) : v3s16(0, 0, 1);
  523. }
  524. }
  525. v3s16 turn_xz(v3s16 olddir, int t)
  526. {
  527. v3s16 dir;
  528. if (t == 0)
  529. {
  530. // Turn right
  531. dir.X = olddir.Z;
  532. dir.Z = -olddir.X;
  533. dir.Y = olddir.Y;
  534. }
  535. else
  536. {
  537. // Turn left
  538. dir.X = -olddir.Z;
  539. dir.Z = olddir.X;
  540. dir.Y = olddir.Y;
  541. }
  542. return dir;
  543. }
  544. v3s16 random_turn(PseudoRandom &random, v3s16 olddir)
  545. {
  546. int turn = random.range(0, 2);
  547. v3s16 dir;
  548. if (turn == 0)
  549. {
  550. // Go straight
  551. dir = olddir;
  552. }
  553. else if (turn == 1)
  554. // Turn right
  555. dir = turn_xz(olddir, 0);
  556. else
  557. // Turn left
  558. dir = turn_xz(olddir, 1);
  559. return dir;
  560. }
  561. int dir_to_facedir(v3s16 d) {
  562. if (abs(d.X) > abs(d.Z))
  563. return d.X < 0 ? 3 : 1;
  564. else
  565. return d.Z < 0 ? 2 : 0;
  566. }