mapgen_dungeon_markets.lua 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. local goblin_enabled = minetest.settings:get_bool("commoditymarket_enable_goblin_market")
  2. local under_enabled = minetest.settings:get_bool("commoditymarket_enable_under_market")
  3. local goblin_prob = tonumber(minetest.settings:get("commoditymarket_goblin_market_dungeon_prob")) or 0.25
  4. local under_prob = tonumber(minetest.settings:get("commoditymarket_under_market_dungeon_prob")) or 0.1
  5. local goblin_max = tonumber(minetest.settings:get("commoditymarket_goblin_market_dungeon_max")) or 100
  6. local goblin_min = tonumber(minetest.settings:get("commoditymarket_goblin_market_dungeon_min")) or -400
  7. local under_max = tonumber(minetest.settings:get("commoditymarket_under_market_dungeon_max")) or -500
  8. local under_min = tonumber(minetest.settings:get("commoditymarket_under_market_dungeon_min")) or -31000
  9. local bad_goblin_range = goblin_min >= goblin_max
  10. local bad_under_range = under_min >= under_max
  11. if bad_goblin_range then
  12. minetest.log("error", "[commoditymarket] Goblin market dungeon generation range has a higher minimum y than maximum y")
  13. end
  14. if bad_under_range then
  15. minetest.log("error", "[commoditymarket] Undermarket dungeon generation range has a higher minimum y than maximum y")
  16. end
  17. local gen_goblin = goblin_enabled and goblin_prob > 0 and not bad_goblin_range
  18. local gen_under = under_enabled and under_prob > 0 and not bad_under_range
  19. if not (gen_goblin or gen_under) then
  20. return
  21. end
  22. -------------------------------------------------------
  23. -- The following is shamelessly copied from dungeon_loot and tweaked for placing markets instead of chests
  24. --Licensed under the MIT License (MIT) Copyright (C) 2017 sfan5
  25. minetest.set_gen_notify({dungeon = true, temple = true})
  26. local function noise3d_integer(noise, pos)
  27. return math.abs(math.floor(noise:get_3d(pos) * 0x7fffffff))
  28. end
  29. local is_wall = function(node)
  30. return node.name ~= "air" and node.name ~= "ignore"
  31. end
  32. local function find_walls(cpos)
  33. local dirs = {{x=1, z=0}, {x=-1, z=0}, {x=0, z=1}, {x=0, z=-1}}
  34. local get_node = minetest.get_node
  35. local ret = {}
  36. local mindist = {x=0, z=0}
  37. local min = function(a, b) return a ~= 0 and math.min(a, b) or b end
  38. for _, dir in ipairs(dirs) do
  39. for i = 1, 9 do -- 9 = max room size / 2
  40. local pos = vector.add(cpos, {x=dir.x*i, y=0, z=dir.z*i})
  41. -- continue in that direction until we find a wall-like node
  42. local node = get_node(pos)
  43. if is_wall(node) then
  44. local front_below = vector.subtract(pos, {x=dir.x, y=1, z=dir.z})
  45. local above = vector.add(pos, {x=0, y=1, z=0})
  46. -- check that it:
  47. --- is at least 2 nodes high (not a staircase)
  48. --- has a floor
  49. if is_wall(get_node(front_below)) and is_wall(get_node(above)) then
  50. pos = vector.subtract(pos, {x=dir.x, y=0, z=dir.z}) -- move goblin markets one node away from the wall
  51. table.insert(ret, {pos = pos, facing = {x=-dir.x, y=0, z=-dir.z}})
  52. if dir.z == 0 then
  53. mindist.x = min(mindist.x, i-1)
  54. else
  55. mindist.z = min(mindist.z, i-1)
  56. end
  57. end
  58. -- abort even if it wasn't a wall cause something is in the way
  59. break
  60. end
  61. end
  62. end
  63. return {
  64. walls = ret,
  65. size = {x=mindist.x*2, z=mindist.z*2},
  66. cpos = cpos,
  67. }
  68. end
  69. minetest.register_on_generated(function(minp, maxp, blockseed)
  70. local min_y = minp.y
  71. local max_y = maxp.y
  72. local gen_goblin_range = gen_goblin and not (min_y > goblin_max or max_y < goblin_min)
  73. local gen_under_range = gen_under and not (min_y > under_max or max_y < under_min)
  74. if not (gen_goblin_range or gen_under_range) then
  75. -- out of both ranges
  76. return
  77. end
  78. local gennotify = minetest.get_mapgen_object("gennotify")
  79. local poslist = gennotify["dungeon"] or {}
  80. for _, entry in ipairs(gennotify["temple"] or {}) do
  81. table.insert(poslist, entry)
  82. end
  83. if #poslist == 0 then return end
  84. local noise = minetest.get_perlin(151994, 4, 0.5, 1)
  85. local rand = PcgRandom(noise3d_integer(noise, poslist[1]))
  86. local rooms = {}
  87. -- process at most 8 rooms to keep runtime of this predictable
  88. local num_process = math.min(#poslist, 8)
  89. for i = 1, num_process do
  90. local room = find_walls(poslist[i])
  91. -- skip small rooms and everything that doesn't at least have 3 walls
  92. if math.min(room.size.x, room.size.z) >= 4 and #room.walls >= 3 then
  93. table.insert(rooms, room)
  94. end
  95. end
  96. if #rooms == 0 then return end
  97. if gen_under_range and rand:next(0, 2147483647)/2147483647 < under_prob then
  98. -- choose a random room
  99. local room = rooms[rand:next(1, #rooms)]
  100. local under_loc = room.cpos
  101. -- put undermarkets in the center of the room
  102. if minetest.get_node(under_loc).name == "air"
  103. and is_wall(vector.subtract(under_loc, {x=0, y=1, z=0})) then
  104. minetest.add_node(under_loc, {name="commoditymarket:under_market"})
  105. end
  106. end
  107. if gen_goblin_range and rand:next(0, 2147483647)/2147483647 < goblin_prob then
  108. -- choose a random room
  109. local room = rooms[rand:next(1, #rooms)]
  110. -- choose place somewhere in front of any of the walls
  111. local wall = room.walls[rand:next(1, #room.walls)]
  112. local v, vi -- vector / axis that runs alongside the wall
  113. if wall.facing.x ~= 0 then
  114. v, vi = {x=0, y=0, z=1}, "z"
  115. else
  116. v, vi = {x=1, y=0, z=0}, "x"
  117. end
  118. local marketpos = vector.add(wall.pos, wall.facing)
  119. local off = rand:next(-room.size[vi]/2 + 1, room.size[vi]/2 - 1)
  120. marketpos = vector.add(marketpos, vector.multiply(v, off))
  121. if minetest.get_node(marketpos).name == "air" then
  122. -- make it face inwards to the room
  123. local facedir = minetest.dir_to_facedir(vector.multiply(wall.facing, -1))
  124. minetest.add_node(marketpos, {name = "commoditymarket:goblin_market", param2 = facedir})
  125. end
  126. end
  127. end)