generator.lua 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. cavegen = {}
  2. cavegen.modpath = minetest.get_modpath("cavegen")
  3. -- Caverealm noise, choses when cave biomes can spawn and what is left stone.
  4. local noise1param3d = {
  5. offset = 0,
  6. scale = 1,
  7. spread = {x=128, y=128, z=128},
  8. seed = 708291,
  9. octaves = 6,
  10. persist = 0.7,
  11. lacunarity = 2
  12. }
  13. -- Vein noise. Choses size & frequency of floor or roof ore veins.
  14. local noise2param3d = {
  15. offset = 0,
  16. scale = 1,
  17. spread = {x=128, y=128, z=128},
  18. seed = 48791,
  19. octaves = 4,
  20. persist = 0.5,
  21. lacunarity = 2
  22. }
  23. -- Biome noise, used to chose what cave biome should generate.
  24. local noise3param3d = {
  25. offset = 0,
  26. scale = 1,
  27. spread = {x=512, y=512, z=512},
  28. seed = 127726,
  29. octaves = 4,
  30. persist = 0.7,
  31. lacunarity = 2
  32. }
  33. -- Content-IDs for the voxel manipulator.
  34. local c_air = minetest.get_content_id("air")
  35. local c_stone = minetest.get_content_id("default:stone")
  36. local c_cobble = minetest.get_content_id("cavestuff:cobble") -- Special nodetype.
  37. local c_mossy_cobble = minetest.get_content_id("default:mossycobble")
  38. local c_cobble_moss = minetest.get_content_id("cavestuff:cobble_with_moss")
  39. local c_cobble_lichen = minetest.get_content_id("cavestuff:cobble_with_lichen")
  40. local c_cobble_algae = minetest.get_content_id("cavestuff:cobble_with_algae")
  41. local c_cobble_salt = minetest.get_content_id("cavestuff:cobble_with_salt")
  42. local c_coal_block = minetest.get_content_id("default:coalblock")
  43. local c_quartz_block = minetest.get_content_id("quartz:block")
  44. local c_dark_obsidian = minetest.get_content_id("cavestuff:dark_obsidian")
  45. local c_thin_ice = minetest.get_content_id("ice:thin_ice")
  46. local c_ice = minetest.get_content_id("default:ice")
  47. local c_sandy_ice = minetest.get_content_id("sand:sand_with_ice_crystals")
  48. -- These tables are updated per chunk-generation iteration.
  49. -- Keeping them external improves performance according to MT Lua docs.
  50. local data = {} -- Voxelmanip data table external for performance.
  51. local noisemap1 = {}
  52. local noisemap2 = {}
  53. local noisemap3 = {}
  54. -- Mapgen notes: this algorithm does not create any caves. It populates caves
  55. -- already generated by the C++ mapgen. Use huge Valleys caves for best results.
  56. cavegen.generate = function(vm, minp, maxp, seed)
  57. -- Do not run for chunks above or below the caverealm region.
  58. if minp.y > -5000 or maxp.y < -23000 then
  59. return
  60. end
  61. -- Grab the voxel manipulator.
  62. local emin, emax = vm:get_emerged_area()
  63. vm:get_data(data)
  64. local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
  65. local area2 = VoxelArea:new{MinEdge=minp, MaxEdge=maxp} -- Noise area.
  66. local pr = PseudoRandom(seed + 71)
  67. local x1 = maxp.x
  68. local y1 = maxp.y
  69. local z1 = maxp.z
  70. local x0 = minp.x
  71. local y0 = minp.y
  72. local z0 = minp.z
  73. --[[
  74. -- Pre-pass: clean up C++ mapgen flat-slab leftovers.
  75. for z = emin.z, emax.z do
  76. for x = emin.x, emax.x do
  77. for y = emin.y, emax.y do
  78. local vp = area:index(x, y, z)
  79. -- Get the type already generated at this position.
  80. local ip = data[vp]
  81. -- Note: sometimes these will be nil, because of accessing outside the array.
  82. -- (We are scanning through the emin/emax range.)
  83. local iu = data[area:index(x, y+1, z)]
  84. local id = data[area:index(x, y-1, z)]
  85. -- HACK:
  86. -- Get rid of the <BEEP> flat horizontal slabs that appear at chunk top/bot edges
  87. -- whenever emerge threads are more than 1. We have to do this *indiscriminately*,
  88. -- which unfortunately modifies the terrain shape more than is actually necessary.
  89. if ip == c_stone then
  90. if (id == c_air or id == c_ignore) and (iu == c_air or iu == c_ignore) then
  91. data[vp] = c_air
  92. end
  93. end
  94. end
  95. end
  96. end
  97. --]]
  98. -- Compute side lengths.
  99. local side_len_x = ((x1-x0)+1)
  100. local side_len_y = ((y1-y0)+1)
  101. local side_len_z = ((z1-z0)+1)
  102. local sides3D = {x=side_len_x, y=side_len_y, z=side_len_z}
  103. local bp3d = {x=x0, y=y0, z=z0}
  104. local perlin1 = minetest.get_perlin_map(noise1param3d, sides3D)
  105. perlin1:get_3d_map_flat(bp3d, noisemap1)
  106. local perlin2 = minetest.get_perlin_map(noise2param3d, sides3D)
  107. perlin2:get_3d_map_flat(bp3d, noisemap2)
  108. local perlin3 = minetest.get_perlin_map(noise3param3d, sides3D)
  109. perlin3:get_3d_map_flat(bp3d, noisemap3)
  110. local floor = math.floor
  111. local ceil = math.ceil
  112. local abs = math.abs
  113. local clamp = function(v, l, u)
  114. if v < l then return l end
  115. if v > u then return u end
  116. return v
  117. end
  118. -- Compute caverealm threshold. 0 = no caverealm, 1 = max possible.
  119. local compute_threshold = function(y)
  120. -- Height values outside outer range result in 0.
  121. if y > -5000 then
  122. return 0
  123. elseif y > -17000 then
  124. -- Y is below -5k and above -10k. Need fade!
  125. local a = (y+5000)/12000 -- Get value between 1 and 0.
  126. return abs(a)
  127. elseif y > -22000 then
  128. return 1
  129. elseif y > -23000 then
  130. -- Y is below -22k and above -23k. Need fade!
  131. local a = (y+23000)/1000 -- Get value between 1 and 0.
  132. return a
  133. else
  134. return 0
  135. end
  136. end
  137. -- These tables will be filled with locations for decorations,
  138. -- calculated by the voxel manipulator code. After the voxel manipulator
  139. -- runs, decorations should be placed at locations in these tables.
  140. local glowstrings = {}
  141. local rooficicles = {}
  142. local floorcicles = {}
  143. local mycenashrom = {}
  144. local fungusshrom = {}
  145. local cavespikeps = {}
  146. local saltcrystal = {}
  147. local moongemspos = {}
  148. local sapphrspike = {} -- Sapphire spikes.
  149. local emraldspike = {} -- Emerald spikes.
  150. local emraldspik2 = {} -- Emerald spikes.
  151. local rubyspikes1 = {}
  152. local amethystspk = {}
  153. local mesespikes1 = {}
  154. local swaterwells = {}
  155. for z = z0, z1 do
  156. for x = x0, x1 do
  157. for y = y0, y1 do
  158. -- Get index into 3D noise array.
  159. local ni3 = area2:index(x, y, z)
  160. local n1 = noisemap1[ni3]
  161. local n2 = noisemap2[ni3]
  162. local n3 = noisemap3[ni3]
  163. local threshold = compute_threshold(y)
  164. local rn = clamp(abs(n1), 0, 1) -- Caverealm noise.
  165. if rn <= threshold then
  166. local vp = area:index(x, y, z)
  167. local vu = area:index(x, y+1, z)
  168. local vd = area:index(x, y-1, z)
  169. local ip = data[vp]
  170. local iu = data[vu]
  171. local id = data[vd]
  172. -- If found cave ceiling.
  173. if ip == c_stone and id == c_air then
  174. data[vp] = c_cobble -- Plain cobble on roof.
  175. if abs(n2) < 0.04 then
  176. data[vd] = c_quartz_block
  177. elseif abs(n2) < 0.09 then
  178. data[vd] = c_coal_block
  179. elseif pr:next(1, 100) == 1 then -- Glow worms.
  180. glowstrings[#glowstrings+1] = {x=x, y=y-1, z=z}
  181. elseif pr:next(1, 100) == 1 then -- Icicles
  182. rooficicles[#rooficicles+1] = {x=x, y=y-1, z=z}
  183. data[vp] = c_thin_ice -- Override cobble roof.
  184. end
  185. end
  186. if rn <= threshold*0.8 then
  187. -- If found raw cave floor.
  188. -- We check floor after ceiling in case floor and ceiling
  189. -- intersect. This way, floor material takes precedence.
  190. if ip == c_stone and iu == c_air then
  191. data[vp] = c_cobble -- Plain cobble under floor surface.
  192. if abs(n2) < 0.05 then
  193. data[vu] = c_dark_obsidian
  194. else
  195. -- Chose floor type to place. Floor type determines biome.
  196. local bn = abs(n3)
  197. if bn < 0.1 then
  198. data[vu] = c_cobble_moss
  199. elseif bn < 0.3 then
  200. data[vu] = c_cobble_algae
  201. elseif bn < 0.6 then
  202. data[vu] = c_cobble_lichen
  203. elseif bn < 0.8 then
  204. data[vu] = c_cobble_salt
  205. elseif bn < 0.9 then
  206. data[vu] = c_sandy_ice
  207. else
  208. data[vu] = c_thin_ice
  209. if bn > 1.0 then
  210. data[vp] = c_ice -- Override cobble floor.
  211. end
  212. end
  213. end
  214. end
  215. end
  216. -- Floor & roof types have changed, re-read them.
  217. ip = data[vp]
  218. iu = data[vu]
  219. id = data[vd]
  220. -- If found generated ice cave floor.
  221. -- Generate position tables for ice biome decorations.
  222. if ip == c_thin_ice and iu == c_air then
  223. if pr:next(1, 100) == 1 then
  224. floorcicles[#floorcicles+1] = {x=x, y=y+1, z=z}
  225. data[vp] = c_ice
  226. end
  227. end
  228. -- If found floor of biome suitable for mushrooms.
  229. -- Generate position tables for mushrooms.
  230. if (ip == c_cobble_moss or
  231. ip == c_cobble_algae or
  232. ip == c_cobble_lichen) and iu == c_air then
  233. if pr:next(1, 100) == 1 then
  234. mycenashrom[#mycenashrom+1] = {x=x, y=y+1, z=z}
  235. data[vp] = c_mossy_cobble
  236. elseif pr:next(1, 70) == 1 then
  237. fungusshrom[#fungusshrom+1] = {x=x, y=y+1, z=z}
  238. data[vp] = c_mossy_cobble
  239. end
  240. end
  241. if ip == c_cobble_salt and iu == c_air then
  242. if pr:next(1, 100) == 1 then
  243. saltcrystal[#saltcrystal+1] = {x=x, y=y+1, z=z}
  244. end
  245. end
  246. if ip == c_cobble_lichen and iu == c_air then
  247. if pr:next(1, 200) == 1 then
  248. moongemspos[#moongemspos+1] = {x=x, y=y+1, z=z}
  249. end
  250. end
  251. -- Water wells.
  252. if pr:next(1, 40) == 1 then
  253. if iu == c_air and ip == c_cobble_moss then
  254. swaterwells[#swaterwells+1] = {x=x, y=y, z=z}
  255. end
  256. end
  257. -- Chance for spikes is deceptively high.
  258. -- Most of these locations will be rejected due to ground checks later.
  259. if pr:next(1, 40) == 1 then
  260. if iu == c_air then
  261. if ip == c_cobble_lichen then
  262. sapphrspike[#sapphrspike+1] = {x=x, y=y+1, z=z}
  263. end
  264. if ip == c_cobble_moss then
  265. emraldspike[#emraldspike+1] = {x=x, y=y+1, z=z}
  266. end
  267. if ip == c_cobble_algae then
  268. emraldspik2[#emraldspik2+1] = {x=x, y=y+1, z=z}
  269. end
  270. end
  271. -- Ceiling spikes.
  272. if ip == c_cobble and id == c_air then
  273. local which = pr:next(1, 3)
  274. if which == 1 then
  275. mesespikes1[#mesespikes1+1] = {x=x, y=y-1, z=z}
  276. elseif which == 2 then
  277. rubyspikes1[#rubyspikes1+1] = {x=x, y=y-1, z=z}
  278. elseif which == 3 then
  279. amethystspk[#amethystspk+1] = {x=x, y=y-1, z=z}
  280. end
  281. end
  282. end
  283. -- If found raw stone floor (edges of caverealm biomes).
  284. -- Generate positions for rock spikes.
  285. if ip == c_stone and iu == c_air then
  286. if pr:next(1, 40) == 1 then
  287. cavespikeps[#cavespikeps+1] = {x=x, y=y+1, z=z}
  288. end
  289. end
  290. end
  291. end -- For all in Y coordinates.
  292. end -- For all in X coordinates.
  293. end -- For all in Z coordinates.
  294. -- Finalize voxel manipulator.
  295. vm:set_data(data)
  296. vm:set_lighting({day=0, night=0})
  297. vm:calc_lighting()
  298. vm:update_liquids()
  299. --vm:write_to_map()
  300. -- Place glow worms.
  301. -- Glow worms come in columns of nodes,
  302. -- so this needs a special algorithm.
  303. for k, v in ipairs(glowstrings) do
  304. local len = pr:next(1, pr:next(2, 5))
  305. for i=0, len, 1 do
  306. local pos = {x=v.x, y=v.y-i, z=v.z}
  307. if minetest.get_node(pos).name == "air" then
  308. minetest.set_node(pos, {name="cavestuff:glow_worm"})
  309. else
  310. break
  311. end
  312. end
  313. end
  314. -- Function for quickly placing 1-node sized decorations.
  315. local place_decorations = function(nn, tt)
  316. for k, v in ipairs(tt) do
  317. local pos = {x=v.x, y=v.y, z=v.z}
  318. if minetest.get_node(pos).name == "air" then
  319. minetest.set_node(pos, {name=nn})
  320. end
  321. end
  322. end
  323. place_decorations("cavestuff:icicle_down", rooficicles)
  324. place_decorations("cavestuff:icicle_up", floorcicles)
  325. place_decorations("cavestuff:mycena", mycenashrom)
  326. place_decorations("cavestuff:fungus", fungusshrom)
  327. -- Place stone spikes.
  328. for k, v in ipairs(cavespikeps) do
  329. local pos = {x=v.x, y=v.y, z=v.z}
  330. if minetest.get_node(pos).name == "air" then
  331. minetest.set_node(pos, {name="cavestuff:spike" .. pr:next(1, 4)})
  332. end
  333. end
  334. -- Place salt crystals.
  335. for k, v in ipairs(saltcrystal) do
  336. local pos = {x=v.x, y=v.y, z=v.z}
  337. if minetest.get_node(pos).name == "air" then
  338. minetest.set_node(pos, {name="cavestuff:saltcrystal" .. pr:next(1, 4)})
  339. end
  340. end
  341. -- Place moon gems.
  342. for k, v in ipairs(moongemspos) do
  343. local pos = {x=v.x, y=v.y, z=v.z}
  344. if minetest.get_node(pos).name == "air" then
  345. minetest.set_node(pos, {name="cavestuff:bluecrystal" .. pr:next(1, 4)})
  346. end
  347. end
  348. local place_spike_up = function(gname, basename, spikename, postable)
  349. for k, v in ipairs(postable) do
  350. -- Check foundation to see if a spike can be placed here.
  351. local floorcheck = {
  352. {x=v.x, y=v.y-1, z=v.z},
  353. {x=v.x-1, y=v.y-1, z=v.z},
  354. {x=v.x+1, y=v.y-1, z=v.z},
  355. {x=v.x, y=v.y-1, z=v.z-1},
  356. {x=v.x, y=v.y-1, z=v.z+1},
  357. }
  358. for i, j in ipairs(floorcheck) do
  359. if minetest.get_node(j).name ~= gname then
  360. return -- Bad foundation.
  361. end
  362. end
  363. -- Foundation is solid.
  364. -- Build crystaline base.
  365. local crystalbase = {
  366. {x=v.x, y=v.y, z=v.z},
  367. {x=v.x-1, y=v.y, z=v.z},
  368. {x=v.x+1, y=v.y, z=v.z},
  369. {x=v.x, y=v.y, z=v.z-1},
  370. {x=v.x, y=v.y, z=v.z+1},
  371. {x=v.x-1, y=v.y, z=v.z-1},
  372. {x=v.x+1, y=v.y, z=v.z-1},
  373. {x=v.x-1, y=v.y, z=v.z+1},
  374. {x=v.x+1, y=v.y, z=v.z+1},
  375. {x=v.x-2, y=v.y, z=v.z},
  376. {x=v.x+2, y=v.y, z=v.z},
  377. {x=v.x, y=v.y, z=v.z-2},
  378. {x=v.x, y=v.y, z=v.z+2},
  379. }
  380. for i, j in ipairs(crystalbase) do
  381. minetest.set_node(j, {name=basename})
  382. end
  383. -- Build upwards pointing spike.
  384. local l = pr:next(7, 20)
  385. local l1 = pr:next(floor(l/3), floor((l/3)*2))
  386. local l2 = pr:next(floor(l/3), floor((l/3)*2))
  387. local l3 = pr:next(floor(l/3), floor((l/3)*2))
  388. local l4 = pr:next(floor(l/3), floor((l/3)*2))
  389. local buildspike = function(pos, len)
  390. for i=0, len, 1 do
  391. local p = {x=pos.x, y=pos.y+i, z=pos.z}
  392. -- Spikes grow through everything,
  393. -- but quit if at unloaded chunk border.
  394. if minetest.get_node(p).name == "ignore" then return end
  395. minetest.set_node(p, {name=spikename})
  396. end
  397. end
  398. buildspike({x=v.x, y=v.y+1, z=v.z}, l)
  399. buildspike({x=v.x-1, y=v.y+1, z=v.z}, l1)
  400. buildspike({x=v.x+1, y=v.y+1, z=v.z}, l2)
  401. buildspike({x=v.x, y=v.y+1, z=v.z-1}, l3)
  402. buildspike({x=v.x, y=v.y+1, z=v.z+1}, l4)
  403. end
  404. end
  405. local place_spike_down = function(gname, basename, spikename, postable)
  406. for k, v in ipairs(postable) do
  407. -- Check foundation to see if a spike can be placed here.
  408. local floorcheck = {
  409. {x=v.x, y=v.y+1, z=v.z},
  410. {x=v.x-1, y=v.y+1, z=v.z},
  411. {x=v.x+1, y=v.y+1, z=v.z},
  412. {x=v.x, y=v.y+1, z=v.z-1},
  413. {x=v.x, y=v.y+1, z=v.z+1},
  414. }
  415. for i, j in ipairs(floorcheck) do
  416. if minetest.get_node(j).name ~= gname then
  417. return -- Bad foundation.
  418. end
  419. end
  420. -- Foundation is solid.
  421. -- Build crystaline base.
  422. local crystalbase = {
  423. {x=v.x, y=v.y, z=v.z},
  424. {x=v.x-1, y=v.y, z=v.z},
  425. {x=v.x+1, y=v.y, z=v.z},
  426. {x=v.x, y=v.y, z=v.z-1},
  427. {x=v.x, y=v.y, z=v.z+1},
  428. {x=v.x-1, y=v.y, z=v.z-1},
  429. {x=v.x+1, y=v.y, z=v.z-1},
  430. {x=v.x-1, y=v.y, z=v.z+1},
  431. {x=v.x+1, y=v.y, z=v.z+1},
  432. {x=v.x-2, y=v.y, z=v.z},
  433. {x=v.x+2, y=v.y, z=v.z},
  434. {x=v.x, y=v.y, z=v.z-2},
  435. {x=v.x, y=v.y, z=v.z+2},
  436. }
  437. for i, j in ipairs(crystalbase) do
  438. minetest.set_node(j, {name=basename})
  439. end
  440. -- Build downwards pointing spike.
  441. local l = pr:next(7, 20)
  442. local l1 = pr:next(floor(l/3), floor((l/3)*2))
  443. local l2 = pr:next(floor(l/3), floor((l/3)*2))
  444. local l3 = pr:next(floor(l/3), floor((l/3)*2))
  445. local l4 = pr:next(floor(l/3), floor((l/3)*2))
  446. local buildspike = function(pos, len)
  447. for i=0, len, 1 do
  448. local p = {x=pos.x, y=pos.y-i, z=pos.z}
  449. -- Spikes grow through everything,
  450. -- but quit if at unloaded chunk border.
  451. if minetest.get_node(p).name == "ignore" then return end
  452. minetest.set_node(p, {name=spikename})
  453. end
  454. end
  455. buildspike({x=v.x, y=v.y-1, z=v.z}, l)
  456. buildspike({x=v.x-1, y=v.y-1, z=v.z}, l1)
  457. buildspike({x=v.x+1, y=v.y-1, z=v.z}, l2)
  458. buildspike({x=v.x, y=v.y-1, z=v.z-1}, l3)
  459. buildspike({x=v.x, y=v.y-1, z=v.z+1}, l4)
  460. end
  461. end
  462. local place_water_well = function(gname, postable)
  463. for k, v in ipairs(postable) do
  464. -- Check foundation to see if a spike can be placed here.
  465. local floorcheck = {
  466. {x=v.x, y=v.y, z=v.z},
  467. {x=v.x-1, y=v.y, z=v.z},
  468. {x=v.x+1, y=v.y, z=v.z},
  469. {x=v.x, y=v.y, z=v.z-1},
  470. {x=v.x, y=v.y, z=v.z+1},
  471. }
  472. for i, j in ipairs(floorcheck) do
  473. if minetest.get_node(j).name ~= gname then
  474. return -- Bad foundation.
  475. end
  476. end
  477. local offset = vector.new(-2, 0, -2)
  478. local pos = vector.add(v, offset)
  479. local path = cavegen.modpath .. "/caverealm_freshwater_well.mts"
  480. minetest.place_schematic_on_vmanip(vm, pos, path, "random", nil, true)
  481. end
  482. end
  483. place_spike_up(
  484. "cavestuff:cobble_with_lichen",
  485. "cavestuff:glow_sapphire_ore",
  486. "cavestuff:glow_sapphire",
  487. sapphrspike)
  488. place_spike_up(
  489. "cavestuff:cobble_with_moss",
  490. "cavestuff:glow_emerald_ore",
  491. "cavestuff:glow_emerald",
  492. emraldspike)
  493. place_spike_up(
  494. "cavestuff:cobble_with_algae",
  495. "cavestuff:glow_emerald_ore",
  496. "cavestuff:glow_emerald",
  497. emraldspik2)
  498. place_spike_down(
  499. "cavestuff:cobble",
  500. "cavestuff:glow_ruby_ore",
  501. "cavestuff:glow_ruby",
  502. rubyspikes1)
  503. place_spike_down(
  504. "cavestuff:cobble",
  505. "cavestuff:glow_amethyst_ore",
  506. "cavestuff:glow_amethyst",
  507. amethystspk)
  508. place_spike_down(
  509. "cavestuff:cobble",
  510. "default:stone_with_mese",
  511. "cavestuff:glow_mese",
  512. mesespikes1)
  513. place_water_well(
  514. "cavestuff:cobble_with_moss",
  515. swaterwells)
  516. end
  517. minetest.register_on_generated(function(...)
  518. cavegen.generate(...)
  519. end)